2021-12-21 15:25:19 +00:00
|
|
|
package kucoin
|
2021-12-21 16:30:16 +00:00
|
|
|
|
|
|
|
import (
|
2021-12-22 17:32:02 +00:00
|
|
|
"fmt"
|
2021-12-23 05:39:15 +00:00
|
|
|
"hash/fnv"
|
2021-12-22 13:06:21 +00:00
|
|
|
"math"
|
|
|
|
"strings"
|
2021-12-22 17:32:02 +00:00
|
|
|
"time"
|
2021-12-22 13:06:21 +00:00
|
|
|
|
2021-12-21 16:30:16 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
func toGlobalBalanceMap(accounts []kucoinapi.Account) types.BalanceMap {
|
|
|
|
balances := types.BalanceMap{}
|
|
|
|
|
|
|
|
// for now, we only return the trading account
|
|
|
|
for _, account := range accounts {
|
|
|
|
switch account.Type {
|
|
|
|
case kucoinapi.AccountTypeTrade:
|
|
|
|
balances[account.Currency] = types.Balance{
|
|
|
|
Currency: account.Currency,
|
|
|
|
Available: account.Available,
|
|
|
|
Locked: account.Holds,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return balances
|
|
|
|
}
|
|
|
|
|
2021-12-22 13:06:21 +00:00
|
|
|
func toGlobalSymbol(symbol string) string {
|
|
|
|
return strings.ReplaceAll(symbol, "-", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
func toGlobalMarket(m kucoinapi.Symbol) types.Market {
|
|
|
|
symbol := toGlobalSymbol(m.Symbol)
|
|
|
|
return types.Market{
|
|
|
|
Symbol: symbol,
|
|
|
|
LocalSymbol: m.Symbol,
|
|
|
|
PricePrecision: int(math.Log10(m.PriceIncrement.Float64())), // convert 0.0001 to 4
|
|
|
|
VolumePrecision: int(math.Log10(m.BaseIncrement.Float64())),
|
|
|
|
QuoteCurrency: m.QuoteCurrency,
|
|
|
|
BaseCurrency: m.BaseCurrency,
|
|
|
|
MinNotional: m.QuoteMinSize.Float64(),
|
|
|
|
MinAmount: m.QuoteMinSize.Float64(),
|
|
|
|
MinQuantity: m.BaseMinSize.Float64(),
|
|
|
|
MaxQuantity: 0, // not used
|
|
|
|
StepSize: m.BaseIncrement.Float64(),
|
|
|
|
|
|
|
|
MinPrice: 0, // not used
|
|
|
|
MaxPrice: 0, // not used
|
|
|
|
TickSize: m.PriceIncrement.Float64(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func toGlobalTicker(s kucoinapi.Ticker24H) types.Ticker {
|
|
|
|
return types.Ticker{
|
|
|
|
Time: s.Time.Time(),
|
|
|
|
Volume: s.Volume.Float64(),
|
|
|
|
Last: s.Last.Float64(),
|
|
|
|
Open: s.Last.Float64() - s.ChangePrice.Float64(),
|
|
|
|
High: s.High.Float64(),
|
|
|
|
Low: s.Low.Float64(),
|
|
|
|
Buy: s.Buy.Float64(),
|
|
|
|
Sell: s.Sell.Float64(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-22 17:32:02 +00:00
|
|
|
|
|
|
|
// 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++
|
|
|
|
|
2021-12-22 18:49:56 +00:00
|
|
|
var subscribeTopic string
|
2021-12-22 17:32:02 +00:00
|
|
|
switch s.Channel {
|
|
|
|
case types.BookChannel:
|
|
|
|
// see https://docs.kucoin.com/#level-2-market-data
|
2021-12-22 18:49:56 +00:00
|
|
|
subscribeTopic = "/market/level2" + ":" + toLocalSymbol(s.Symbol)
|
2021-12-22 17:32:02 +00:00
|
|
|
|
|
|
|
case types.KLineChannel:
|
2021-12-22 18:49:56 +00:00
|
|
|
subscribeTopic = "/market/candles" + ":" + toLocalSymbol(s.Symbol) + "_" + s.Options.Interval
|
2021-12-22 17:32:02 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("websocket channel %s is not supported by kucoin", s.Channel)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmds = append(cmds, kucoinapi.WebSocketCommand{
|
|
|
|
Id: id,
|
2021-12-22 18:49:56 +00:00
|
|
|
Type: kucoinapi.WebSocketMessageTypeSubscribe,
|
|
|
|
Topic: subscribeTopic,
|
2021-12-22 17:32:02 +00:00
|
|
|
PrivateChannel: false,
|
|
|
|
Response: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmds, nil
|
|
|
|
}
|
|
|
|
|
2021-12-23 05:39:15 +00:00
|
|
|
func hashStringID(s string) uint64 {
|
|
|
|
h := fnv.New64a()
|
|
|
|
h.Write([]byte(s))
|
|
|
|
return h.Sum64()
|
|
|
|
}
|
|
|
|
|
|
|
|
func toGlobalSide(s string) types.SideType {
|
|
|
|
switch s {
|
|
|
|
case "buy":
|
|
|
|
return types.SideTypeBuy
|
|
|
|
case "sell":
|
|
|
|
return types.SideTypeSell
|
|
|
|
}
|
|
|
|
|
|
|
|
return types.SideTypeSelf
|
|
|
|
}
|
|
|
|
|
|
|
|
func toGlobalOrderType(s string) types.OrderType {
|
|
|
|
switch s {
|
|
|
|
case "limit":
|
|
|
|
return types.OrderTypeLimit
|
|
|
|
|
|
|
|
case "stop_limit":
|
|
|
|
return types.OrderTypeStopLimit
|
|
|
|
|
|
|
|
case "market":
|
|
|
|
return types.OrderTypeMarket
|
|
|
|
|
|
|
|
case "stop_market":
|
|
|
|
return types.OrderTypeStopMarket
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|