bbgo_origin/pkg/exchange/kucoin/convert.go

248 lines
5.7 KiB
Go
Raw Normal View History

2021-12-21 15:25:19 +00:00
package kucoin
import (
2021-12-22 17:32:02 +00:00
"fmt"
"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
"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(),
}
}
func toLocalInterval(i types.Interval) string {
switch i {
case types.Interval1m:
return "1min"
case types.Interval5m:
return "5min"
case types.Interval15m:
return "15min"
case types.Interval30m:
return "30min"
case types.Interval1h:
return "1hour"
case types.Interval2h:
return "2hour"
case types.Interval4h:
return "4hour"
case types.Interval6h:
return "6hour"
case types.Interval12h:
return "12hour"
case types.Interval1d:
return "1day"
}
return "1hour"
}
2021-12-22 17:32:02 +00:00
// convertSubscriptions global subscription to local websocket command
func convertSubscriptions(ss []types.Subscription) ([]WebSocketCommand, error) {
2021-12-23 13:09:40 +00:00
var id = time.Now().UnixNano() / int64(time.Millisecond)
var cmds []WebSocketCommand
2021-12-22 17:32:02 +00:00
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:
subscribeTopic = "/market/candles" + ":" + toLocalSymbol(s.Symbol) + "_" + toLocalInterval(types.Interval(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, WebSocketCommand{
2021-12-22 17:32:02 +00:00
Id: id,
Type: WebSocketMessageTypeSubscribe,
2021-12-22 18:49:56 +00:00
Topic: subscribeTopic,
2021-12-22 17:32:02 +00:00
PrivateChannel: false,
Response: true,
})
}
return cmds, nil
}
func hashStringID(s string) uint64 {
h := fnv.New64a()
h.Write([]byte(s))
return h.Sum64()
}
func toGlobalOrderStatus(o kucoinapi.Order) types.OrderStatus {
var status types.OrderStatus
if o.IsActive {
status = types.OrderStatusNew
2021-12-25 17:34:03 +00:00
if o.DealSize > 0 {
status = types.OrderStatusPartiallyFilled
}
} else if o.CancelExist {
status = types.OrderStatusCanceled
} else {
status = types.OrderStatusFilled
}
return status
}
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 ""
}
2021-12-25 16:27:52 +00:00
func toLocalSide(side types.SideType) kucoinapi.SideType {
switch side {
case types.SideTypeBuy:
return kucoinapi.SideTypeBuy
case types.SideTypeSell:
return kucoinapi.SideTypeSell
}
return ""
}
func toGlobalOrder(o kucoinapi.Order) types.Order {
var status = toGlobalOrderStatus(o)
var order = types.Order{
SubmitOrder: types.SubmitOrder{
ClientOrderID: o.ClientOrderID,
Symbol: toGlobalSymbol(o.Symbol),
Side: toGlobalSide(o.Side),
Type: toGlobalOrderType(o.Type),
Quantity: o.Size.Float64(),
Price: o.Price.Float64(),
StopPrice: o.StopPrice.Float64(),
TimeInForce: string(o.TimeInForce),
},
Exchange: types.ExchangeKucoin,
OrderID: hashStringID(o.ID),
UUID: o.ID,
Status: status,
ExecutedQuantity: o.DealSize.Float64(),
IsWorking: o.IsActive,
CreationTime: types.Time(o.CreatedAt.Time()),
UpdateTime: types.Time(o.CreatedAt.Time()), // kucoin does not response updated time
}
return order
}
2021-12-25 17:44:05 +00:00
func toGlobalTrade(fill kucoinapi.Fill) types.Trade {
var trade = types.Trade{
ID: hashStringID(fill.TradeId),
OrderID: hashStringID(fill.OrderId),
Exchange: types.ExchangeKucoin,
Price: fill.Price.Float64(),
Quantity: fill.Size.Float64(),
QuoteQuantity: fill.Funds.Float64(),
Symbol: toGlobalSymbol(fill.Symbol),
Side: toGlobalSide(string(fill.Side)),
IsBuyer: fill.Side == kucoinapi.SideTypeBuy,
IsMaker: fill.Liquidity == kucoinapi.LiquidityTypeMaker,
2021-12-31 05:20:34 +00:00
Time: types.Time(fill.CreatedAt.Time()),
2021-12-25 17:44:05 +00:00
Fee: fill.Fee.Float64(),
FeeCurrency: toGlobalSymbol(fill.FeeCurrency),
}
return trade
}