diff --git a/pkg/exchange/bybit/bybitapi/get_open_orders_request.go b/pkg/exchange/bybit/bybitapi/get_open_orders_request.go index 3f01b717d..e70f6964b 100644 --- a/pkg/exchange/bybit/bybitapi/get_open_orders_request.go +++ b/pkg/exchange/bybit/bybitapi/get_open_orders_request.go @@ -78,7 +78,7 @@ type Order struct { PlaceType string `json:"placeType"` } -//go:generate GetRequest -url "/v5/order/realtime" -type GetOpenOrdersRequest -responseDataType .OrdersResponse +//go:generate GetRequest -url "/v5/order/realtime" -type GetOpenOrdersRequest -responseDataType .OrdersResponse -rateLimiter 1+45/1s type GetOpenOrdersRequest struct { client requestgen.AuthenticatedAPIClient diff --git a/pkg/exchange/bybit/bybitapi/get_open_orders_request_requestgen.go b/pkg/exchange/bybit/bybitapi/get_open_orders_request_requestgen.go index 3b7c29ba4..0654b4c87 100644 --- a/pkg/exchange/bybit/bybitapi/get_open_orders_request_requestgen.go +++ b/pkg/exchange/bybit/bybitapi/get_open_orders_request_requestgen.go @@ -1,4 +1,4 @@ -// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Result -url /v5/order/realtime -type GetOpenOrdersRequest -responseDataType .OrdersResponse"; DO NOT EDIT. +// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Result -url /v5/order/realtime -type GetOpenOrdersRequest -responseDataType .OrdersResponse -rateLimiter 1+45/1s"; DO NOT EDIT. package bybitapi @@ -6,11 +6,14 @@ import ( "context" "encoding/json" "fmt" + "golang.org/x/time/rate" "net/url" "reflect" "regexp" ) +var GetOpenOrdersRequestLimiter = rate.NewLimiter(45.00000045, 1) + func (g *GetOpenOrdersRequest) Category(category Category) *GetOpenOrdersRequest { g.category = category return g @@ -265,6 +268,9 @@ func (g *GetOpenOrdersRequest) GetPath() string { // Do generates the request object and send the request object to the API endpoint func (g *GetOpenOrdersRequest) Do(ctx context.Context) (*OrdersResponse, error) { + if err := GetOpenOrdersRequestLimiter.Wait(ctx); err != nil { + return nil, err + } // no body params var params interface{} @@ -288,15 +294,29 @@ func (g *GetOpenOrdersRequest) Do(ctx context.Context) (*OrdersResponse, error) } var apiResponse APIResponse - if err := response.DecodeJSON(&apiResponse); err != nil { - return nil, err + + type responseUnmarshaler interface { + Unmarshal(data []byte) error + } + + if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok { + if err := unmarshaler.Unmarshal(response.Body); err != nil { + return nil, err + } + } else { + // The line below checks the content type, however, some API server might not send the correct content type header, + // Hence, this is commented for backward compatibility + // response.IsJSON() + if err := response.DecodeJSON(&apiResponse); err != nil { + return nil, err + } } type responseValidator interface { Validate() error } - validator, ok := interface{}(apiResponse).(responseValidator) - if ok { + + if validator, ok := interface{}(&apiResponse).(responseValidator); ok { if err := validator.Validate(); err != nil { return nil, err } diff --git a/pkg/exchange/bybit/convert.go b/pkg/exchange/bybit/convert.go index 4207d7d08..036bbdc4c 100644 --- a/pkg/exchange/bybit/convert.go +++ b/pkg/exchange/bybit/convert.go @@ -76,9 +76,9 @@ func toGlobalOrder(order bybitapi.Order) (*types.Order, error) { return nil, fmt.Errorf("unexpected order id: %s, err: %w", order.OrderId, err) } - qty, err := processMarketBuyQuantity(order) - if err != nil { - return nil, err + price := order.Price + if orderType == types.OrderTypeMarket { + price = order.AvgPrice } return &types.Order{ @@ -87,9 +87,11 @@ func toGlobalOrder(order bybitapi.Order) (*types.Order, error) { Symbol: order.Symbol, Side: side, Type: orderType, - Quantity: qty, - Price: order.Price, - TimeInForce: timeInForce, + // We specified the quantity for the market buy order as the base coin when we submitted the order, + // so we can just use the Qty field. + Quantity: order.Qty, + Price: price, + TimeInForce: timeInForce, }, Exchange: types.ExchangeBybit, OrderID: orderIdNum, diff --git a/pkg/exchange/bybit/exchange.go b/pkg/exchange/bybit/exchange.go index 96ccfbc8b..8ad204749 100644 --- a/pkg/exchange/bybit/exchange.go +++ b/pkg/exchange/bybit/exchange.go @@ -33,9 +33,7 @@ var ( // sharedRateLimiter indicates that the API belongs to the public API. // The default order limiter apply 5 requests per second and a 5 initial bucket // this includes QueryMarkets, QueryTicker, QueryAccountBalances, GetFeeRates - sharedRateLimiter = rate.NewLimiter(rate.Every(time.Second/5), 5) - queryOrderTradeRateLimiter = rate.NewLimiter(rate.Every(time.Second/5), 5) - closedOrderQueryLimiter = rate.NewLimiter(rate.Every(time.Second), 1) + sharedRateLimiter = rate.NewLimiter(rate.Every(time.Second/5), 5) log = logrus.WithFields(logrus.Fields{ "exchange": "bybit", @@ -164,18 +162,24 @@ func (e *Exchange) QueryTickers(ctx context.Context, symbols ...string) (map[str return tickers, nil } +// QueryOpenOrders queries open orders by symbol. +// +// Primarily query unfilled or partially filled orders in real-time, but also supports querying recent 500 closed status +// (Cancelled, Filled) orders. Please see the usage of request param openOnly. +// UTA2.0 can query filled, canceled, and rejected orders to the most recent 500 orders for spot, linear, inverse and +// option categories +// +// The records are sorted by the createdTime from newest to oldest. func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders []types.Order, err error) { cursor := "" + // OpenOnlyOrder: UTA2.0, UTA1.0, classic account query open status orders (e.g., New, PartiallyFilled) only + req := e.client.NewGetOpenOrderRequest().Symbol(symbol).OpenOnly(bybitapi.OpenOnlyOrder).Limit(defaultQueryLimit) for { - req := e.client.NewGetOpenOrderRequest().Symbol(symbol) if len(cursor) != 0 { // the default limit is 20. req = req.Cursor(cursor) } - if err = queryOrderTradeRateLimiter.Wait(ctx); err != nil { - return nil, fmt.Errorf("place order rate limiter wait error: %w", err) - } res, err := req.Do(ctx) if err != nil { return nil, fmt.Errorf("failed to query open orders, err: %w", err)