kucoin: implement NewStream

This commit is contained in:
c9s 2021-12-23 01:32:02 +08:00
parent 0a9575aaca
commit 730ce31e67
3 changed files with 63 additions and 63 deletions

View File

@ -1,8 +1,10 @@
package kucoin package kucoin
import ( import (
"fmt"
"math" "math"
"strings" "strings"
"time"
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi" "github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
@ -64,6 +66,36 @@ func toGlobalTicker(s kucoinapi.Ticker24H) types.Ticker {
} }
} }
func convertSubscription(s types.Subscription) (WebsocketSubscription, error) {
return WebsocketSubscription{}, nil // convertSubscriptions global subscription to local websocket command
func convertSubscriptions(ss []types.Subscription) ([]kucoinapi.WebSocketCommand, error) {
var id = time.Now().UnixMilli()
var cmds []kucoinapi.WebSocketCommand
for _, s := range ss {
id++
var subscribeType string
switch s.Channel {
case types.BookChannel:
// see https://docs.kucoin.com/#level-2-market-data
subscribeType = "/market/level2" + ":" + toLocalSymbol(s.Symbol)
case types.KLineChannel:
subscribeType = "/market/candles" + ":" + toLocalSymbol(s.Symbol) + "_" + s.Options.Interval
default:
return nil, fmt.Errorf("websocket channel %s is not supported by kucoin", s.Channel)
} }
cmds = append(cmds, kucoinapi.WebSocketCommand{
Id: id,
Type: subscribeType,
Topic: "subscribe",
PrivateChannel: false,
Response: true,
})
}
return cmds, nil
}

View File

@ -138,5 +138,5 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) erro
} }
func (e *Exchange) NewStream() types.Stream { func (e *Exchange) NewStream() types.Stream {
panic("implement me") return NewStream(e.client)
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi" "github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/pkg/errors"
) )
const readTimeout = 15 * time.Second const readTimeout = 15 * time.Second
@ -40,8 +41,6 @@ type Stream struct {
publicOnly bool publicOnly bool
} }
type WebsocketSubscription struct{}
func NewStream(client *kucoinapi.RestClient) *Stream { func NewStream(client *kucoinapi.RestClient) *Stream {
stream := &Stream{ stream := &Stream{
client: client, client: client,
@ -50,61 +49,32 @@ func NewStream(client *kucoinapi.RestClient) *Stream {
}, },
} }
stream.OnConnect(func() { stream.OnConnect(stream.handleConnect)
if stream.publicOnly { return stream
var subs []WebsocketSubscription
for _, subscription := range stream.Subscriptions {
sub, err := convertSubscription(subscription)
if err != nil {
log.WithError(err).Errorf("subscription convert error")
continue
} }
subs = append(subs, sub) func (s *Stream) sendSubscriptions() error {
cmds, err := convertSubscriptions(s.Subscriptions)
if err != nil {
return errors.Wrapf(err, "subscription convert error, subscriptions: %+v", s.Subscriptions)
} }
if len(subs) == 0 {
for _, cmd := range cmds {
if err := s.conn.WriteJSON(cmd) ; err != nil {
return errors.Wrapf(err, "subscribe write error, cmd: %+v", cmd)
}
}
return nil
}
func (s *Stream) handleConnect() {
if s.publicOnly {
if err := s.sendSubscriptions() ; err != nil {
log.WithError(err).Errorf("subscription error")
return return
} }
log.Infof("subscribing channels: %+v", subs)
err := stream.conn.WriteJSON(WebsocketOp{
Op: "subscribe",
Args: subs,
})
if err != nil {
log.WithError(err).Error("subscribe error")
} }
} else {
// login as private channel
// sign example:
// sign=CryptoJS.enc.Base64.Stringify(CryptoJS.HmacSHA256(timestamp +'GET'+'/users/self/verify', secretKey))
/*
msTimestamp := strconv.FormatFloat(float64(time.Now().UnixNano())/float64(time.Second), 'f', -1, 64)
payload := msTimestamp + "GET" + "/users/self/verify"
sign := okexapi.Sign(payload, stream.client.Secret)
op := WebsocketOp{
Op: "login",
Args: []WebsocketLogin{
{
Key: stream.client.Key,
Passphrase: stream.client.Passphrase,
Timestamp: msTimestamp,
Sign: sign,
},
},
}
log.Infof("sending login request: %+v", op)
err := stream.conn.WriteJSON(op)
if err != nil {
log.WithError(err).Errorf("can not send login message")
}
*/
}
})
return stream
} }
func (s *Stream) SetPublicOnly() { func (s *Stream) SetPublicOnly() {
@ -225,9 +195,7 @@ func (s *Stream) read(ctx context.Context) {
return return
default: default:
s.connLock.Lock() conn := s.Conn()
conn := s.conn
s.connLock.Unlock()
if err := conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil { if err := conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil {
log.WithError(err).Errorf("set read deadline error: %s", err.Error()) log.WithError(err).Errorf("set read deadline error: %s", err.Error())