diff --git a/pkg/bbgo/active_book.go b/pkg/bbgo/active_book.go index c39067be5..79beaf9c4 100644 --- a/pkg/bbgo/active_book.go +++ b/pkg/bbgo/active_book.go @@ -51,14 +51,14 @@ func (b *LocalActiveOrderBook) orderUpdateHandler(order types.Order) { case types.OrderStatusNew: if order.Quantity == 0 { - log.Debugf("[LocalActiveOrderBook] order status %s, removing %d...", order.Status, order.OrderID) + log.Debugf("[LocalActiveOrderBook] order status %s, removing order %s", order.Status, order) b.Remove(order) } else { b.Update(order) } case types.OrderStatusCanceled, types.OrderStatusRejected: - log.Debugf("[LocalActiveOrderBook] order status %s, removing %d...", order.Status, order.OrderID) + log.Debugf("[LocalActiveOrderBook] order status %s, removing order %s", order.Status, order) b.Remove(order) default: diff --git a/pkg/exchange/kucoin/exchange.go b/pkg/exchange/kucoin/exchange.go index 7a413f685..ebcec2807 100644 --- a/pkg/exchange/kucoin/exchange.go +++ b/pkg/exchange/kucoin/exchange.go @@ -2,6 +2,7 @@ package kucoin import ( "context" + "fmt" "strconv" "time" @@ -133,7 +134,7 @@ func (e *Exchange) QueryTickers(ctx context.Context, symbols ...string) (map[str var supportedIntervals = map[types.Interval]int{ types.Interval1m: 60, types.Interval5m: 60 * 5, - types.Interval15m: 60 * 15, + types.Interval15m: 60 * 15, types.Interval30m: 60 * 30, types.Interval1h: 60 * 60, types.Interval2h: 60 * 60 * 2, @@ -152,7 +153,6 @@ func (e *Exchange) IsSupportedInterval(interval types.Interval) bool { return ok } - func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval types.Interval, options types.KLineQueryOptions) ([]types.KLine, error) { _ = marketDataLimiter.Wait(ctx) @@ -241,6 +241,7 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder SubmitOrder: order, Exchange: types.ExchangeKucoin, OrderID: hashStringID(orderResponse.OrderID), + UUID: orderResponse.OrderID, Status: types.OrderStatusNew, ExecutedQuantity: 0, IsWorking: true, @@ -341,7 +342,10 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err } else if o.ClientOrderID != "" { req.ClientOrderID(o.ClientOrderID) } else { - errs = multierr.Append(errs, errors.New("can not cancel order, either order uuid nor client order id is empty")) + errs = multierr.Append( + errs, + fmt.Errorf("the order uuid or client order id is empty, order: %#v", o), + ) continue } @@ -354,7 +358,7 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err log.Infof("cancelled orders: %v", response.CancelledOrderIDs) } - return errs + return errors.Wrap(errs, "order cancel error") } func (e *Exchange) NewStream() types.Stream { diff --git a/pkg/exchange/kucoin/stream.go b/pkg/exchange/kucoin/stream.go index 219e5be11..aec8baa8c 100644 --- a/pkg/exchange/kucoin/stream.go +++ b/pkg/exchange/kucoin/stream.go @@ -134,7 +134,7 @@ func (s *Stream) handlePrivateOrderEvent(e *WebSocketPrivateOrderEvent) { } switch e.Type { - case "open", "match", "filled": + case "open", "match", "filled", "canceled": status := types.OrderStatusNew if e.Status == "done" { if e.FilledSize == e.Size { @@ -151,11 +151,11 @@ func (s *Stream) handlePrivateOrderEvent(e *WebSocketPrivateOrderEvent) { s.StandardStream.EmitOrderUpdate(types.Order{ SubmitOrder: types.SubmitOrder{ ClientOrderID: e.ClientOid, - Symbol: toGlobalSymbol(e.Symbol), - Side: toGlobalSide(e.Side), - Type: toGlobalOrderType(e.OrderType), - Quantity: e.Size.Float64(), - Price: e.Price.Float64(), + Symbol: toGlobalSymbol(e.Symbol), + Side: toGlobalSide(e.Side), + Type: toGlobalOrderType(e.OrderType), + Quantity: e.Size.Float64(), + Price: e.Price.Float64(), }, Exchange: types.ExchangeKucoin, OrderID: hashStringID(e.OrderId), @@ -378,6 +378,7 @@ func (s *Stream) read(ctx context.Context) { // used for debugging // log.Println(string(message)) + log.Debug(string(message)) e, err := parseWebsocketPayload(message) if err != nil { @@ -387,9 +388,8 @@ func (s *Stream) read(ctx context.Context) { // remove bytes, so we won't print them e.Data = nil - log.Infof("event: %+v", e) - if e != nil && e.Object != nil { + log.Debugf("parsed event data: %+v",e.Object) s.dispatchEvent(e) } } diff --git a/pkg/exchange/kucoin/websocket.go b/pkg/exchange/kucoin/websocket.go index 9724b8909..519e49be7 100644 --- a/pkg/exchange/kucoin/websocket.go +++ b/pkg/exchange/kucoin/websocket.go @@ -121,17 +121,17 @@ func (e *WebSocketCandleEvent) KLine() types.KLine { } type WebSocketPrivateOrderEvent struct { - OrderId string `json:"orderId"` - TradeId string `json:"tradeId"` - Symbol string `json:"symbol"` - OrderType string `json:"orderType"` - Side string `json:"side"` - Type string `json:"type"` - OrderTime types.MillisecondTimestamp `json:"orderTime"` - Price fixedpoint.Value `json:"price"` - Size fixedpoint.Value `json:"size"` - FilledSize fixedpoint.Value `json:"filledSize"` - RemainSize fixedpoint.Value `json:"remainSize"` + OrderId string `json:"orderId"` + TradeId string `json:"tradeId"` + Symbol string `json:"symbol"` + OrderType string `json:"orderType"` + Side string `json:"side"` + Type string `json:"type"` + OrderTime types.NanosecondTimestamp `json:"orderTime"` + Price fixedpoint.Value `json:"price"` + Size fixedpoint.Value `json:"size"` + FilledSize fixedpoint.Value `json:"filledSize"` + RemainSize fixedpoint.Value `json:"remainSize"` Liquidity string `json:"liquidity"` MatchPrice fixedpoint.Value `json:"matchPrice"` diff --git a/pkg/types/order.go b/pkg/types/order.go index 85f90b883..99676894c 100644 --- a/pkg/types/order.go +++ b/pkg/types/order.go @@ -215,12 +215,20 @@ func (o Order) Backup() SubmitOrder { func (o Order) String() string { var orderID string if o.UUID != "" { - orderID = o.UUID + orderID = fmt.Sprintf("UUID %s (%d)", o.UUID, o.OrderID) } else { orderID = strconv.FormatUint(o.OrderID, 10) } - return fmt.Sprintf("ORDER %s %s %s %s %f/%f @ %f -> %s", o.Exchange.String(), orderID, o.Symbol, o.Side, o.ExecutedQuantity, o.Quantity, o.Price, o.Status) + return fmt.Sprintf("ORDER %s %s %s %s %f/%f @ %f -> %s", + o.Exchange.String(), + orderID, + o.Symbol, + o.Side, + o.ExecutedQuantity, + o.Quantity, + o.Price, + o.Status) } // PlainText is used for telegram-styled messages diff --git a/pkg/types/time.go b/pkg/types/time.go index d5bf5573c..7b8606215 100644 --- a/pkg/types/time.go +++ b/pkg/types/time.go @@ -8,6 +8,25 @@ import ( "time" ) +type NanosecondTimestamp time.Time + +func (t NanosecondTimestamp) Time() time.Time { + return time.Time(t) +} + +func (t *NanosecondTimestamp) UnmarshalJSON(data []byte) error { + var v int64 + + var err = json.Unmarshal(data, &v) + if err != nil { + return err + } + + *t = NanosecondTimestamp(time.Unix(0, v)) + return nil +} + + type MillisecondTimestamp time.Time func NewMillisecondTimestampFromInt(i int64) MillisecondTimestamp { @@ -97,6 +116,8 @@ func (t *MillisecondTimestamp) UnmarshalJSON(data []byte) error { return (*time.Time)(t).UnmarshalJSON(data) } + + type Time time.Time var layout = "2006-01-02 15:04:05.999Z07:00"