max: clean up and refactor max stream

This commit is contained in:
c9s 2022-01-02 12:00:06 +08:00
parent cc0e5f71b0
commit e04139a330
6 changed files with 328 additions and 615 deletions

View File

@ -104,7 +104,7 @@ func toGlobalOrderStatus(orderState max.OrderState, executedVolume, remainingVol
}
logger.Errorf("unknown order status: %v", orderState)
log.Errorf("unknown order status: %v", orderState)
return types.OrderStatus(orderState)
}
@ -130,7 +130,7 @@ func toGlobalOrderType(orderType max.OrderType) types.OrderType {
}
logger.Errorf("order convert error, unknown order type: %v", orderType)
log.Errorf("order convert error, unknown order type: %v", orderType)
return types.OrderType(orderType)
}

View File

@ -758,7 +758,7 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
for _, t := range remoteTrades {
localTrade, err := toGlobalTrade(t)
if err != nil {
logger.WithError(err).Errorf("can not convert trade: %+v", t)
log.WithError(err).Errorf("can not convert trade: %+v", t)
continue
}

View File

@ -1,16 +1,7 @@
package max
import (
"context"
"fmt"
"net"
"sync"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
var WebSocketURL = "wss://max-stream.maicoin.com/ws"
@ -40,307 +31,3 @@ type WebsocketCommand struct {
var SubscribeAction = "subscribe"
var UnsubscribeAction = "unsubscribe"
//go:generate callbackgen -type WebSocketService
type WebSocketService struct {
baseURL, key, secret string
mu sync.Mutex
conn *websocket.Conn
reconnectC chan struct{}
// Subscriptions is the subscription request payloads that will be used for sending subscription request
Subscriptions []Subscription
connectCallbacks []func(conn *websocket.Conn)
disconnectCallbacks []func()
errorCallbacks []func(err error)
messageCallbacks []func(message []byte)
bookEventCallbacks []func(e BookEvent)
tradeEventCallbacks []func(e PublicTradeEvent)
kLineEventCallbacks []func(e KLineEvent)
errorEventCallbacks []func(e ErrorEvent)
subscriptionEventCallbacks []func(e SubscriptionEvent)
tradeUpdateEventCallbacks []func(e TradeUpdateEvent)
tradeSnapshotEventCallbacks []func(e TradeSnapshotEvent)
orderUpdateEventCallbacks []func(e OrderUpdateEvent)
orderSnapshotEventCallbacks []func(e OrderSnapshotEvent)
accountSnapshotEventCallbacks []func(e AccountSnapshotEvent)
accountUpdateEventCallbacks []func(e AccountUpdateEvent)
}
func NewWebSocketService(wsURL string, key, secret string) *WebSocketService {
return &WebSocketService{
key: key,
secret: secret,
reconnectC: make(chan struct{}, 1),
baseURL: wsURL,
}
}
func (s *WebSocketService) Connect(ctx context.Context) error {
s.OnConnect(func(c *websocket.Conn) {
if err := s.SendSubscriptionRequest(SubscribeAction); err != nil {
s.EmitError(err)
logger.WithError(err).Error("failed to subscribe")
}
})
// pre-allocate the websocket client, the websocket client can be used for reconnecting.
if err := s.connect(ctx); err != nil {
return err
}
go s.reconnector(ctx)
go s.ping(ctx)
return nil
}
func (s *WebSocketService) ping(ctx context.Context) {
pingTicker := time.NewTicker(5 * time.Second)
defer pingTicker.Stop()
for {
select {
case <-ctx.Done():
log.Debug("ping worker stopped")
return
case <-pingTicker.C:
s.mu.Lock()
conn := s.conn
s.mu.Unlock()
if err := conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(3*time.Second)); err != nil {
log.WithError(err).Error("ping error", err)
s.Reconnect()
}
}
}
}
func (s *WebSocketService) Auth() error {
nonce := time.Now().UnixNano() / int64(time.Millisecond)
auth := &AuthMessage{
Action: "auth",
APIKey: s.key,
Nonce: nonce,
Signature: signPayload(fmt.Sprintf("%d", nonce), s.secret),
ID: uuid.New().String(),
}
return s.conn.WriteJSON(auth)
}
func (s *WebSocketService) connect(ctx context.Context) error {
dialer := websocket.DefaultDialer
conn, _, err := dialer.DialContext(ctx, s.baseURL, nil)
if err != nil {
return err
}
conn.SetReadDeadline(time.Now().Add(15 * time.Second))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(time.Now().Add(15 * time.Second))
return nil
})
s.mu.Lock()
s.conn = conn
s.mu.Unlock()
s.EmitConnect(conn)
go s.read(ctx)
return nil
}
func (s *WebSocketService) emitReconnect() {
select {
case s.reconnectC <- struct{}{}:
default:
}
}
func (s *WebSocketService) reconnector(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-s.reconnectC:
log.Warnf("received reconnect signal, reconnecting...")
time.Sleep(3 * time.Second)
if err := s.connect(ctx); err != nil {
s.emitReconnect()
}
}
}
}
func (s *WebSocketService) read(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
s.mu.Lock()
conn := s.conn
s.mu.Unlock()
if err := conn.SetReadDeadline(time.Now().Add(time.Second * 5)); err != nil {
log.WithError(err).Error("can not set read deadline")
}
mt, msg, err := conn.ReadMessage()
if err != nil {
// if it's a network timeout error, we should re-connect
switch err := err.(type) {
// if it's a websocket related error
case *websocket.CloseError:
if err.Code == websocket.CloseNormalClosure {
return
}
// for unexpected close error, we should re-connect
// emit reconnect to start a new connection
s.EmitDisconnect()
s.emitReconnect()
return
case net.Error:
s.EmitDisconnect()
s.emitReconnect()
return
default:
log.WithError(err).Error("unexpected websocket connection error")
continue
}
}
if mt != websocket.TextMessage {
continue
}
s.EmitMessage(msg)
m, err := ParseMessage(msg)
if err != nil {
s.EmitError(errors.Wrapf(err, "failed to parse message: %s", msg))
continue
}
if m != nil {
s.dispatch(m)
}
}
}
}
func (s *WebSocketService) dispatch(msg interface{}) {
switch e := msg.(type) {
case *BookEvent:
s.EmitBookEvent(*e)
case *PublicTradeEvent:
s.EmitTradeEvent(*e)
case *KLineEvent:
s.EmitKLineEvent(*e)
case *ErrorEvent:
s.EmitErrorEvent(*e)
case *SubscriptionEvent:
s.EmitSubscriptionEvent(*e)
case *TradeSnapshotEvent:
s.EmitTradeSnapshotEvent(*e)
case *TradeUpdateEvent:
s.EmitTradeUpdateEvent(*e)
case *AccountSnapshotEvent:
s.EmitAccountSnapshotEvent(*e)
case *AccountUpdateEvent:
s.EmitAccountUpdateEvent(*e)
case *OrderSnapshotEvent:
s.EmitOrderSnapshotEvent(*e)
case *OrderUpdateEvent:
s.EmitOrderUpdateEvent(*e)
default:
s.EmitError(fmt.Errorf("unsupported %T event: %+v", e, e))
}
}
func (s *WebSocketService) ClearSubscriptions() {
s.Subscriptions = nil
}
func (s *WebSocketService) Reconnect() {
logger.Info("reconnecting...")
s.emitReconnect()
}
// Subscribe is a helper method for building subscription request from the internal mapping types.
// (Internal public method)
func (s *WebSocketService) Subscribe(channel, market string, options SubscribeOptions) {
s.AddSubscription(Subscription{
Channel: channel,
Market: market,
Depth: options.Depth,
Resolution: options.Resolution,
})
}
// AddSubscription adds the subscription request to the buffer, these requests will be sent to the server right after connecting to the endpoint.
func (s *WebSocketService) AddSubscription(subscription Subscription) {
s.Subscriptions = append(s.Subscriptions, subscription)
}
func (s *WebSocketService) Resubscribe() {
// Calling Resubscribe() by websocket is not enough to refresh orderbook.
// We still need to get orderbook snapshot by rest client.
// Therefore Reconnect() is used to simplify implementation.
logger.Info("resubscribing all subscription...")
if err := s.SendSubscriptionRequest(UnsubscribeAction); err != nil {
logger.WithError(err).Error("failed to unsubscribe")
}
if err := s.SendSubscriptionRequest(SubscribeAction); err != nil {
logger.WithError(err).Error("failed to unsubscribe")
}
}
func (s *WebSocketService) SendSubscriptionRequest(action string) error {
request := WebsocketCommand{
Action: action,
Subscriptions: s.Subscriptions,
}
logger.Debugf("sending websocket subscription: %+v", request)
if err := s.conn.WriteJSON(request); err != nil {
return errors.Wrap(err, "Failed to send subscribe event")
}
return nil
}
// Close web socket connection
func (s *WebSocketService) Close() error {
return s.conn.Close()
}

View File

@ -1,157 +0,0 @@
// Code generated by "callbackgen -type WebSocketService"; DO NOT EDIT.
package max
import (
"github.com/gorilla/websocket"
)
func (s *WebSocketService) OnConnect(cb func(conn *websocket.Conn)) {
s.connectCallbacks = append(s.connectCallbacks, cb)
}
func (s *WebSocketService) EmitConnect(conn *websocket.Conn) {
for _, cb := range s.connectCallbacks {
cb(conn)
}
}
func (s *WebSocketService) OnDisconnect(cb func()) {
s.disconnectCallbacks = append(s.disconnectCallbacks, cb)
}
func (s *WebSocketService) EmitDisconnect() {
for _, cb := range s.disconnectCallbacks {
cb()
}
}
func (s *WebSocketService) OnError(cb func(err error)) {
s.errorCallbacks = append(s.errorCallbacks, cb)
}
func (s *WebSocketService) EmitError(err error) {
for _, cb := range s.errorCallbacks {
cb(err)
}
}
func (s *WebSocketService) OnMessage(cb func(message []byte)) {
s.messageCallbacks = append(s.messageCallbacks, cb)
}
func (s *WebSocketService) EmitMessage(message []byte) {
for _, cb := range s.messageCallbacks {
cb(message)
}
}
func (s *WebSocketService) OnBookEvent(cb func(e BookEvent)) {
s.bookEventCallbacks = append(s.bookEventCallbacks, cb)
}
func (s *WebSocketService) EmitBookEvent(e BookEvent) {
for _, cb := range s.bookEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnTradeEvent(cb func(e PublicTradeEvent)) {
s.tradeEventCallbacks = append(s.tradeEventCallbacks, cb)
}
func (s *WebSocketService) EmitTradeEvent(e PublicTradeEvent) {
for _, cb := range s.tradeEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnKLineEvent(cb func(e KLineEvent)) {
s.kLineEventCallbacks = append(s.kLineEventCallbacks, cb)
}
func (s *WebSocketService) EmitKLineEvent(e KLineEvent) {
for _, cb := range s.kLineEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnErrorEvent(cb func(e ErrorEvent)) {
s.errorEventCallbacks = append(s.errorEventCallbacks, cb)
}
func (s *WebSocketService) EmitErrorEvent(e ErrorEvent) {
for _, cb := range s.errorEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnSubscriptionEvent(cb func(e SubscriptionEvent)) {
s.subscriptionEventCallbacks = append(s.subscriptionEventCallbacks, cb)
}
func (s *WebSocketService) EmitSubscriptionEvent(e SubscriptionEvent) {
for _, cb := range s.subscriptionEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnTradeUpdateEvent(cb func(e TradeUpdateEvent)) {
s.tradeUpdateEventCallbacks = append(s.tradeUpdateEventCallbacks, cb)
}
func (s *WebSocketService) EmitTradeUpdateEvent(e TradeUpdateEvent) {
for _, cb := range s.tradeUpdateEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnTradeSnapshotEvent(cb func(e TradeSnapshotEvent)) {
s.tradeSnapshotEventCallbacks = append(s.tradeSnapshotEventCallbacks, cb)
}
func (s *WebSocketService) EmitTradeSnapshotEvent(e TradeSnapshotEvent) {
for _, cb := range s.tradeSnapshotEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnOrderUpdateEvent(cb func(e OrderUpdateEvent)) {
s.orderUpdateEventCallbacks = append(s.orderUpdateEventCallbacks, cb)
}
func (s *WebSocketService) EmitOrderUpdateEvent(e OrderUpdateEvent) {
for _, cb := range s.orderUpdateEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnOrderSnapshotEvent(cb func(e OrderSnapshotEvent)) {
s.orderSnapshotEventCallbacks = append(s.orderSnapshotEventCallbacks, cb)
}
func (s *WebSocketService) EmitOrderSnapshotEvent(e OrderSnapshotEvent) {
for _, cb := range s.orderSnapshotEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnAccountSnapshotEvent(cb func(e AccountSnapshotEvent)) {
s.accountSnapshotEventCallbacks = append(s.accountSnapshotEventCallbacks, cb)
}
func (s *WebSocketService) EmitAccountSnapshotEvent(e AccountSnapshotEvent) {
for _, cb := range s.accountSnapshotEventCallbacks {
cb(e)
}
}
func (s *WebSocketService) OnAccountUpdateEvent(cb func(e AccountUpdateEvent)) {
s.accountUpdateEventCallbacks = append(s.accountUpdateEventCallbacks, cb)
}
func (s *WebSocketService) EmitAccountUpdateEvent(e AccountUpdateEvent) {
for _, cb := range s.accountUpdateEventCallbacks {
cb(e)
}
}

View File

@ -2,11 +2,15 @@ package max
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
"strconv"
"time"
"github.com/gorilla/websocket"
"github.com/google/uuid"
max "github.com/c9s/bbgo/pkg/exchange/max/maxapi"
"github.com/c9s/bbgo/pkg/fixedpoint"
@ -14,172 +18,184 @@ import (
"github.com/c9s/bbgo/pkg/util"
)
var logger = log.WithField("exchange", "max")
//go:generate callbackgen -type Stream
type Stream struct {
types.StandardStream
websocketService *max.WebSocketService
key, secret string
bookEventCallbacks []func(e max.BookEvent)
tradeEventCallbacks []func(e max.PublicTradeEvent)
kLineEventCallbacks []func(e max.KLineEvent)
errorEventCallbacks []func(e max.ErrorEvent)
subscriptionEventCallbacks []func(e max.SubscriptionEvent)
tradeUpdateEventCallbacks []func(e max.TradeUpdateEvent)
tradeSnapshotEventCallbacks []func(e max.TradeSnapshotEvent)
orderUpdateEventCallbacks []func(e max.OrderUpdateEvent)
orderSnapshotEventCallbacks []func(e max.OrderSnapshotEvent)
accountSnapshotEventCallbacks []func(e max.AccountSnapshotEvent)
accountUpdateEventCallbacks []func(e max.AccountUpdateEvent)
}
func NewStream(key, secret string) *Stream {
stream := &Stream{
StandardStream: types.NewStandardStream(),
key: key,
secret: secret,
}
stream.SetEndpointCreator(stream.getEndpoint)
stream.SetParser(max.ParseMessage)
stream.SetDispatcher(stream.dispatchEvent)
stream.OnConnect(stream.handleConnect)
stream.OnKLineEvent(stream.handleKLineEvent)
stream.OnOrderSnapshotEvent(stream.handleOrderSnapshotEvent)
stream.OnOrderUpdateEvent(stream.handleOrderUpdateEvent)
stream.OnTradeUpdateEvent(stream.handleTradeEvent)
stream.OnBookEvent(stream.handleBookEvent)
stream.OnAccountSnapshotEvent(stream.handleAccountSnapshotEvent)
stream.OnAccountUpdateEvent(stream.handleAccountUpdateEvent)
return stream
}
func (s *Stream) getEndpoint(ctx context.Context) (string, error) {
url := os.Getenv("MAX_API_WS_URL")
if url == "" {
url = max.WebSocketURL
}
return url, nil
}
wss := max.NewWebSocketService(url, key, secret)
stream := &Stream{
websocketService: wss,
func (s *Stream) handleConnect() {
if s.PublicOnly {
cmd := &max.WebsocketCommand{
Action: "subscribe",
}
for _, sub := range s.Subscriptions {
var err error
var depth int
if len(sub.Options.Depth) > 0 {
depth, err = strconv.Atoi(sub.Options.Depth)
if err != nil {
log.WithError(err).Errorf("depth parse error, given %v", sub.Options.Depth)
continue
}
}
cmd.Subscriptions = append(cmd.Subscriptions, max.Subscription{
Channel: string(sub.Channel),
Market: toLocalSymbol(sub.Symbol),
Depth: depth,
Resolution: sub.Options.Interval,
})
}
s.Conn.WriteJSON(cmd)
} else {
nonce := time.Now().UnixNano() / int64(time.Millisecond)
auth := &max.AuthMessage{
Action: "auth",
APIKey: s.key,
Nonce: nonce,
Signature: signPayload(fmt.Sprintf("%d", nonce), s.secret),
ID: uuid.New().String(),
}
if err := s.Conn.WriteJSON(auth); err != nil {
log.WithError(err).Error("failed to send auth request")
}
}
}
wss.OnConnect(func(conn *websocket.Conn) {
if key == "" || secret == "" {
log.Warn("MAX API key or secret is empty, will not send authentication command")
} else {
if err := wss.Auth(); err != nil {
wss.EmitError(err)
logger.WithError(err).Error("failed to send auth request")
}
}
})
func (s *Stream) handleKLineEvent(e max.KLineEvent) {
kline := e.KLine.KLine()
s.EmitKLine(kline)
if kline.Closed {
s.EmitKLineClosed(kline)
}
}
wss.OnDisconnect(stream.EmitDisconnect)
wss.OnMessage(func(message []byte) {
logger.Debugf("M: %s", message)
})
wss.OnKLineEvent(func(e max.KLineEvent) {
kline := e.KLine.KLine()
stream.EmitKLine(kline)
if kline.Closed {
stream.EmitKLineClosed(kline)
}
})
wss.OnOrderSnapshotEvent(func(e max.OrderSnapshotEvent) {
for _, o := range e.Orders {
globalOrder, err := toGlobalOrderUpdate(o)
if err != nil {
log.WithError(err).Error("websocket order snapshot convert error")
continue
}
stream.EmitOrderUpdate(*globalOrder)
}
})
wss.OnOrderUpdateEvent(func(e max.OrderUpdateEvent) {
for _, o := range e.Orders {
globalOrder, err := toGlobalOrderUpdate(o)
if err != nil {
log.WithError(err).Error("websocket order update convert error")
continue
}
stream.EmitOrderUpdate(*globalOrder)
}
})
wss.OnTradeUpdateEvent(func(e max.TradeUpdateEvent) {
for _, tradeUpdate := range e.Trades {
trade, err := convertWebSocketTrade(tradeUpdate)
if err != nil {
log.WithError(err).Error("websocket trade update convert error")
return
}
stream.EmitTradeUpdate(*trade)
}
})
wss.OnBookEvent(func(e max.BookEvent) {
newBook, err := e.OrderBook()
func (s *Stream) handleOrderSnapshotEvent(e max.OrderSnapshotEvent) {
for _, o := range e.Orders {
globalOrder, err := toGlobalOrderUpdate(o)
if err != nil {
logger.WithError(err).Error("book convert error")
log.WithError(err).Error("websocket order snapshot convert error")
continue
}
s.EmitOrderUpdate(*globalOrder)
}
}
func (s *Stream) handleOrderUpdateEvent(e max.OrderUpdateEvent) {
for _, o := range e.Orders {
globalOrder, err := toGlobalOrderUpdate(o)
if err != nil {
log.WithError(err).Error("websocket order update convert error")
continue
}
s.EmitOrderUpdate(*globalOrder)
}
}
func (s *Stream) handleTradeEvent(e max.TradeUpdateEvent) {
for _, tradeUpdate := range e.Trades {
trade, err := convertWebSocketTrade(tradeUpdate)
if err != nil {
log.WithError(err).Error("websocket trade update convert error")
return
}
newBook.Symbol = toGlobalSymbol(e.Market)
switch e.Event {
case "snapshot":
stream.EmitBookSnapshot(newBook)
case "update":
stream.EmitBookUpdate(newBook)
}
})
wss.OnConnect(func(conn *websocket.Conn) {
stream.EmitConnect()
})
wss.OnAccountSnapshotEvent(func(e max.AccountSnapshotEvent) {
snapshot := map[string]types.Balance{}
for _, bm := range e.Balances {
balance, err := bm.Balance()
if err != nil {
continue
}
snapshot[toGlobalCurrency(balance.Currency)] = *balance
}
stream.EmitBalanceSnapshot(snapshot)
})
wss.OnAccountUpdateEvent(func(e max.AccountUpdateEvent) {
snapshot := map[string]types.Balance{}
for _, bm := range e.Balances {
balance, err := bm.Balance()
if err != nil {
continue
}
snapshot[toGlobalCurrency(balance.Currency)] = *balance
}
stream.EmitBalanceUpdate(snapshot)
})
wss.OnError(func(err error) {
log.WithError(err).Error("websocket error")
})
return stream
s.EmitTradeUpdate(*trade)
}
}
func (s *Stream) Subscribe(channel types.Channel, symbol string, options types.SubscribeOptions) {
opt := max.SubscribeOptions{}
if len(options.Depth) > 0 {
depth, err := strconv.Atoi(options.Depth)
if err != nil {
panic(err)
}
opt.Depth = depth
}
if len(options.Interval) > 0 {
opt.Resolution = options.Interval
}
s.websocketService.Subscribe(string(channel), toLocalSymbol(symbol), opt)
}
func (s *Stream) Connect(ctx context.Context) error {
err := s.websocketService.Connect(ctx)
func (s *Stream) handleBookEvent(e max.BookEvent) {
newBook, err := e.OrderBook()
if err != nil {
return err
log.WithError(err).Error("book convert error")
return
}
s.EmitStart()
return nil
newBook.Symbol = toGlobalSymbol(e.Market)
switch e.Event {
case "snapshot":
s.EmitBookSnapshot(newBook)
case "update":
s.EmitBookUpdate(newBook)
}
}
func (s *Stream) Close() error {
return s.websocketService.Close()
func (s *Stream) handleAccountSnapshotEvent(e max.AccountSnapshotEvent) {
snapshot := map[string]types.Balance{}
for _, bm := range e.Balances {
balance, err := bm.Balance()
if err != nil {
continue
}
snapshot[balance.Currency] = *balance
}
s.EmitBalanceSnapshot(snapshot)
}
func (s *Stream) handleAccountUpdateEvent(e max.AccountUpdateEvent) {
snapshot := map[string]types.Balance{}
for _, bm := range e.Balances {
balance, err := bm.Balance()
if err != nil {
continue
}
snapshot[toGlobalCurrency(balance.Currency)] = *balance
}
s.EmitBalanceUpdate(snapshot)
}
func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) {
@ -253,3 +269,53 @@ func toGlobalOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
}, nil
}
func (s *Stream) dispatchEvent(e interface{}) {
switch e := e.(type) {
case *max.BookEvent:
s.EmitBookEvent(*e)
case *max.PublicTradeEvent:
s.EmitTradeEvent(*e)
case *max.KLineEvent:
s.EmitKLineEvent(*e)
case *max.ErrorEvent:
s.EmitErrorEvent(*e)
case *max.SubscriptionEvent:
s.EmitSubscriptionEvent(*e)
case *max.TradeSnapshotEvent:
s.EmitTradeSnapshotEvent(*e)
case *max.TradeUpdateEvent:
s.EmitTradeUpdateEvent(*e)
case *max.AccountSnapshotEvent:
s.EmitAccountSnapshotEvent(*e)
case *max.AccountUpdateEvent:
s.EmitAccountUpdateEvent(*e)
case *max.OrderSnapshotEvent:
s.EmitOrderSnapshotEvent(*e)
case *max.OrderUpdateEvent:
s.EmitOrderUpdateEvent(*e)
default:
log.Errorf("unsupported %T event: %+v", e, e)
}
}
func signPayload(payload string, secret string) string {
var sig = hmac.New(sha256.New, []byte(secret))
_, err := sig.Write([]byte(payload))
if err != nil {
return ""
}
return hex.EncodeToString(sig.Sum(nil))
}

View File

@ -0,0 +1,117 @@
// Code generated by "callbackgen -type Stream"; DO NOT EDIT.
package max
import (
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
)
func (s *Stream) OnBookEvent(cb func(e max.BookEvent)) {
s.bookEventCallbacks = append(s.bookEventCallbacks, cb)
}
func (s *Stream) EmitBookEvent(e max.BookEvent) {
for _, cb := range s.bookEventCallbacks {
cb(e)
}
}
func (s *Stream) OnTradeEvent(cb func(e max.PublicTradeEvent)) {
s.tradeEventCallbacks = append(s.tradeEventCallbacks, cb)
}
func (s *Stream) EmitTradeEvent(e max.PublicTradeEvent) {
for _, cb := range s.tradeEventCallbacks {
cb(e)
}
}
func (s *Stream) OnKLineEvent(cb func(e max.KLineEvent)) {
s.kLineEventCallbacks = append(s.kLineEventCallbacks, cb)
}
func (s *Stream) EmitKLineEvent(e max.KLineEvent) {
for _, cb := range s.kLineEventCallbacks {
cb(e)
}
}
func (s *Stream) OnErrorEvent(cb func(e max.ErrorEvent)) {
s.errorEventCallbacks = append(s.errorEventCallbacks, cb)
}
func (s *Stream) EmitErrorEvent(e max.ErrorEvent) {
for _, cb := range s.errorEventCallbacks {
cb(e)
}
}
func (s *Stream) OnSubscriptionEvent(cb func(e max.SubscriptionEvent)) {
s.subscriptionEventCallbacks = append(s.subscriptionEventCallbacks, cb)
}
func (s *Stream) EmitSubscriptionEvent(e max.SubscriptionEvent) {
for _, cb := range s.subscriptionEventCallbacks {
cb(e)
}
}
func (s *Stream) OnTradeUpdateEvent(cb func(e max.TradeUpdateEvent)) {
s.tradeUpdateEventCallbacks = append(s.tradeUpdateEventCallbacks, cb)
}
func (s *Stream) EmitTradeUpdateEvent(e max.TradeUpdateEvent) {
for _, cb := range s.tradeUpdateEventCallbacks {
cb(e)
}
}
func (s *Stream) OnTradeSnapshotEvent(cb func(e max.TradeSnapshotEvent)) {
s.tradeSnapshotEventCallbacks = append(s.tradeSnapshotEventCallbacks, cb)
}
func (s *Stream) EmitTradeSnapshotEvent(e max.TradeSnapshotEvent) {
for _, cb := range s.tradeSnapshotEventCallbacks {
cb(e)
}
}
func (s *Stream) OnOrderUpdateEvent(cb func(e max.OrderUpdateEvent)) {
s.orderUpdateEventCallbacks = append(s.orderUpdateEventCallbacks, cb)
}
func (s *Stream) EmitOrderUpdateEvent(e max.OrderUpdateEvent) {
for _, cb := range s.orderUpdateEventCallbacks {
cb(e)
}
}
func (s *Stream) OnOrderSnapshotEvent(cb func(e max.OrderSnapshotEvent)) {
s.orderSnapshotEventCallbacks = append(s.orderSnapshotEventCallbacks, cb)
}
func (s *Stream) EmitOrderSnapshotEvent(e max.OrderSnapshotEvent) {
for _, cb := range s.orderSnapshotEventCallbacks {
cb(e)
}
}
func (s *Stream) OnAccountSnapshotEvent(cb func(e max.AccountSnapshotEvent)) {
s.accountSnapshotEventCallbacks = append(s.accountSnapshotEventCallbacks, cb)
}
func (s *Stream) EmitAccountSnapshotEvent(e max.AccountSnapshotEvent) {
for _, cb := range s.accountSnapshotEventCallbacks {
cb(e)
}
}
func (s *Stream) OnAccountUpdateEvent(cb func(e max.AccountUpdateEvent)) {
s.accountUpdateEventCallbacks = append(s.accountUpdateEventCallbacks, cb)
}
func (s *Stream) EmitAccountUpdateEvent(e max.AccountUpdateEvent) {
for _, cb := range s.accountUpdateEventCallbacks {
cb(e)
}
}