From 0c0afe288bfaeafbfb6979ba83d0f7f10dec18b9 Mon Sep 17 00:00:00 2001 From: Samuel Reid <43227667+cranktakular@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:31:21 +1100 Subject: [PATCH] Bitget is Sisyphean --- exchanges/bitget/bitget.go | 379 ++++++++++++++++++--------- exchanges/bitget/bitget_test.go | 194 ++++++++------ exchanges/bitget/bitget_types.go | 129 +++++---- exchanges/bitget/bitget_websocket.go | 13 +- exchanges/bitget/bitget_wrapper.go | 30 ++- 5 files changed, 469 insertions(+), 276 deletions(-) diff --git a/exchanges/bitget/bitget.go b/exchanges/bitget/bitget.go index 0151d91b7dc..dc1033aa1a2 100644 --- a/exchanges/bitget/bitget.go +++ b/exchanges/bitget/bitget.go @@ -27,16 +27,16 @@ const ( bitgetAPIURL = "https://api.bitget.com/api/v2/" // Public endpoints - bitgetPublic = "public/" - bitgetAnnouncements = "annoucements" // sic - bitgetTime = "time" - bitgetMarket = "market/" - bitgetWhaleNetFlow = "whale-net-flow" - bitgetTakerBuySell = "taker-buy-sell" - bitgetPositionLongShort = "position-long-short" - // bitgetLongShortRatio = "long-short-ratio" - // bitgetLoanGrowth = "loan-growth" - // bitgetIsolatedBorrowRate = "isolated-borrow-rate" + bitgetPublic = "public/" + bitgetAnnouncements = "annoucements" // sic + bitgetTime = "time" + bitgetMarket = "market/" + bitgetWhaleNetFlow = "whale-net-flow" + bitgetTakerBuySell = "taker-buy-sell" + bitgetPositionLongShort = "position-long-short" + bitgetLongShortRatio = "long-short-ratio" + bitgetLoanGrowth = "loan-growth" + bitgetIsolatedBorrowRate = "isolated-borrow-rate" bitgetLongShort = "long-short" bitgetFundFlow = "fund-flow" bitgetSupportSymbols = "support-symbols" @@ -104,6 +104,8 @@ const ( bitgetConvertCoinList = "bgb-convert-coin-list" bitgetBGBConvertRecords = "bgb-convert-records" bitgetPlaceOrder = "/place-order" + bitgetCancelReplaceOrder = "/cancel-replace-order" + bitgetBatchCancelReplaceOrder = "/batch-cancel-replace-order" bitgetCancelOrder = "/cancel-order" bitgetBatchOrders = "/batch-orders" bitgetBatchCancel = "/batch-cancel-order" @@ -487,7 +489,7 @@ func (bi *Bitget) GetMerchantP2POrders(ctx context.Context, startTime, endTime t } // GetMerchantAdvertisementList returns information on a variety of merchant advertisements -func (bi *Bitget) GetMerchantAdvertisementList(ctx context.Context, startTime, endTime time.Time, limit, pagination, adNum, payMethodID int64, status, side, cryptoCurrency, fiatCurrency, orderBy, sourceType string) ([]P2PAdListResp, error) { +func (bi *Bitget) GetMerchantAdvertisementList(ctx context.Context, startTime, endTime time.Time, limit, pagination, adNum, payMethodID int64, status, side, cryptoCurrency, fiatCurrency, orderBy, sourceType string) (*P2PAdListResp, error) { var params Params params.Values = make(url.Values) err := params.prepareDateString(startTime, endTime, false, true) @@ -511,11 +513,9 @@ func (bi *Bitget) GetMerchantAdvertisementList(ctx context.Context, startTime, e params.Values.Set("orderBy", orderBy) params.Values.Set("sourceType", sourceType) var resp struct { - Data struct { - AdList []P2PAdListResp `json:"advList"` - } `json:"data"` + Data P2PAdListResp `json:"data"` } - return resp.Data.AdList, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, bitgetP2P+bitgetAdvList, params.Values, nil, &resp) + return &resp.Data, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, bitgetP2P+bitgetAdvList, params.Values, nil, &resp) } // GetSpotWhaleNetFlow returns the amount whales have been trading in a specified pair recently @@ -562,50 +562,52 @@ func (bi *Bitget) GetFuturesPositionRatios(ctx context.Context, pair, period str return resp.PosRatFutureResp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) } -// When queried, the exchange claims that this endpoint doesn't exist, despite having a documentation page -// // GetMarginPositionRatios returns the ratio of long to short positions for a specified pair in margin accounts -// func (bi *Bitget) GetMarginPositionRatios(ctx context.Context, pair, period, currency string) (*PosRatMarginResp, error) { -// if pair == "" { -// return nil, errPairEmpty -// } -// vals := url.Values{} -// vals.Set("symbol", pair) -// vals.Set("period", period) -// vals.Set("coin", currency) -// path := bitgetMix + bitgetMarket + bitgetLongShortRatio -// var resp *PosRatMarginResp -// return resp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) -// } - -// When queried, the exchange claims that this endpoint doesn't exist, despite having a documentation page -// // GetMarginLoanGrowth returns the growth rate of borrowed funds for a specified pair in margin accounts -// func (bi *Bitget) GetMarginLoanGrowth(ctx context.Context, pair, period, currency string) (*LoanGrowthResp, error) { -// if pair == "" { -// return nil, errPairEmpty -// } -// vals := url.Values{} -// vals.Set("symbol", pair) -// vals.Set("period", period) -// vals.Set("coin", currency) -// path := bitgetMix + bitgetMarket + bitgetLoanGrowth -// var resp *LoanGrowthResp -// return resp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) -// } - -// When queried, the exchange claims that this endpoint doesn't exist, despite having a documentation page -// // GetIsolatedBorrowingRatio returns the ratio of borrowed funds between base and quote currencies, after -// // converting to USDT, within isolated margin accounts -// func (bi *Bitget) GetIsolatedBorrowingRatio(ctx context.Context, pair, period string) (*BorrowRatioResp, error) { -// if pair == "" { -// return nil, errPairEmpty -// } -// vals := url.Values{} -// vals.Set("symbol", pair) -// vals.Set("period", period) -// path := bitgetMix + bitgetMarket + bitgetIsolatedBorrowRate -// var resp *BorrowRatioResp -// return resp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) -// } +// GetMarginPositionRatios returns the ratio of long to short positions for a specified pair in margin accounts +func (bi *Bitget) GetMarginPositionRatios(ctx context.Context, pair, period, currency string) ([]PosRatMarginResp, error) { + if pair == "" { + return nil, errPairEmpty + } + vals := url.Values{} + vals.Set("symbol", pair) + vals.Set("period", period) + vals.Set("coin", currency) + path := bitgetMargin + bitgetMarket + bitgetLongShortRatio + var resp struct { + PosRatMarginResp []PosRatMarginResp `json:"data"` + } + return resp.PosRatMarginResp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) +} + +// GetMarginLoanGrowth returns the growth rate of borrowed funds for a specified pair in margin accounts +func (bi *Bitget) GetMarginLoanGrowth(ctx context.Context, pair, period, currency string) ([]LoanGrowthResp, error) { + if pair == "" { + return nil, errPairEmpty + } + vals := url.Values{} + vals.Set("symbol", pair) + vals.Set("period", period) + vals.Set("coin", currency) + path := bitgetMargin + bitgetMarket + bitgetLoanGrowth + var resp struct { + LoanGrowthResp []LoanGrowthResp `json:"data"` + } + return resp.LoanGrowthResp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) +} + +// GetIsolatedBorrowingRatio returns the ratio of borrowed funds between base and quote currencies, after converting to USDT, within isolated margin accounts +func (bi *Bitget) GetIsolatedBorrowingRatio(ctx context.Context, pair, period string) ([]BorrowRatioResp, error) { + if pair == "" { + return nil, errPairEmpty + } + vals := url.Values{} + vals.Set("symbol", pair) + vals.Set("period", period) + path := bitgetMargin + bitgetMarket + bitgetIsolatedBorrowRate + var resp struct { + BorrowRatioResp []BorrowRatioResp `json:"data"` + } + return resp.BorrowRatioResp, bi.SendHTTPRequest(ctx, exchange.RestSpot, Rate1, path, vals, &resp) +} // GetFuturesRatios returns the ratio of long to short positions for a specified pair func (bi *Bitget) GetFuturesRatios(ctx context.Context, pair, period string) ([]RatioResp, error) { @@ -787,13 +789,14 @@ func (bi *Bitget) ModifyAPIKey(ctx context.Context, subaccountID, passphrase, la return nil, errSubaccountEmpty } path := bitgetUser + bitgetModify + bitgetVirtualSubaccount + "-" + bitgetAPIKey - req := make(map[string]any) - req["subAccountUid"] = subaccountID - req["passphrase"] = passphrase - req["label"] = label - req["subAccountApiKey"] = apiKey - req["ipList"] = whiteList - req["permList"] = permList + req := map[string]any{ + "subAccountUid": subaccountID, + "passphrase": passphrase, + "label": label, + "subAccountApiKey": apiKey, + "ipList": whiteList, + "permList": permList, + } var resp struct { AlterAPIKeyResp `json:"data"` } @@ -1112,7 +1115,7 @@ func (bi *Bitget) GetSpotMarketTrades(ctx context.Context, pair string, startTim } // PlaceSpotOrder places a spot order on the exchange -func (bi *Bitget) PlaceSpotOrder(ctx context.Context, pair, side, orderType, strategy, clientOrderID string, price, amount float64, isCopyTradeLeader bool) (*OrderIDResp, error) { +func (bi *Bitget) PlaceSpotOrder(ctx context.Context, pair, side, orderType, strategy, clientOrderID, stpMode string, price, amount, triggerPrice, presetTPPrice, executeTPPrice, presetSLPrice, executeSLPrice float64, isCopyTradeLeader bool, acceptableDelay time.Duration) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -1132,26 +1135,101 @@ func (bi *Bitget) PlaceSpotOrder(ctx context.Context, pair, side, orderType, str return nil, errAmountEmpty } req := map[string]any{ - "symbol": pair, - "side": side, - "orderType": orderType, - "force": strategy, - "price": strconv.FormatFloat(price, 'f', -1, 64), - "size": strconv.FormatFloat(amount, 'f', -1, 64), - "clientOid": clientOrderID, + "symbol": pair, + "side": side, + "orderType": orderType, + "force": strategy, + "price": strconv.FormatFloat(price, 'f', -1, 64), + "size": strconv.FormatFloat(amount, 'f', -1, 64), + "stpMode": stpMode, + "requestTime": strconv.FormatInt(time.Now().UnixMilli(), 10), + } + if triggerPrice != 0 { + req["triggerPrice"] = strconv.FormatFloat(triggerPrice, 'f', -1, 64) + req["tpslType"] = "tpsl" + } else { + req["clientOid"] = clientOrderID + } + if acceptableDelay != 0 { + req["receiveWindow"] = acceptableDelay.Milliseconds() + } + if presetTPPrice != 0 { + req["presetTakeProfitPrice"] = strconv.FormatFloat(presetTPPrice, 'f', -1, 64) + req["executeTakeProfitPrice"] = strconv.FormatFloat(executeTPPrice, 'f', -1, 64) + } + if presetSLPrice != 0 { + req["presetStopLossPrice"] = strconv.FormatFloat(presetSLPrice, 'f', -1, 64) + req["executeStopLossPrice"] = strconv.FormatFloat(executeSLPrice, 'f', -1, 64) } path := bitgetSpot + bitgetTrade + bitgetPlaceOrder - var resp *OrderIDResp + var resp struct { + OrderIDStruct `json:"data"` + } // I suspect the two rate limits have to do with distinguishing ordinary traders, and traders who are also copy trade leaders. Since this isn't detectable, it'll be handled in the relevant functions through a bool rLim := Rate10 if isCopyTradeLeader { rLim = Rate1 } - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) +} + +// CancelAndPlaceSpotOrder cancels an order and places a new one on the exchange +func (bi *Bitget) CancelAndPlaceSpotOrder(ctx context.Context, pair, oldClientOrderID, newClientOrderID string, price, amount, presetTPPrice, executeTPPrice, presetSLPrice, executeSLPrice float64, orderID int64) (*CancelAndPlaceResp, error) { + if pair == "" { + return nil, errPairEmpty + } + if oldClientOrderID == "" && orderID == 0 { + return nil, errOrderClientEmpty + } + if price == 0 { + return nil, errPriceEmpty + } + req := map[string]any{ + "symbol": pair, + "price": strconv.FormatFloat(price, 'f', -1, 64), + "size": strconv.FormatFloat(amount, 'f', -1, 64), + } + if oldClientOrderID != "" { + req["clientOid"] = oldClientOrderID + } + if orderID != 0 { + req["orderId"] = strconv.FormatInt(orderID, 10) + } + if newClientOrderID != "" { + req["newClientOid"] = newClientOrderID + } + if presetTPPrice != 0 { + req["presetTakeProfitPrice"] = strconv.FormatFloat(presetTPPrice, 'f', -1, 64) + req["executeTakeProfitPrice"] = strconv.FormatFloat(executeTPPrice, 'f', -1, 64) + } + if presetSLPrice != 0 { + req["presetStopLossPrice"] = strconv.FormatFloat(presetSLPrice, 'f', -1, 64) + req["executeStopLossPrice"] = strconv.FormatFloat(executeSLPrice, 'f', -1, 64) + } + path := bitgetSpot + bitgetTrade + bitgetCancelReplaceOrder + var resp struct { + CancelAndPlaceResp `json:"data"` + } + return &resp.CancelAndPlaceResp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodPost, path, nil, req, &resp) +} + +// BatchCancelAndPlaceSpotOrders cancels and places up to fifty orders on the exchange +func (bi *Bitget) BatchCancelAndPlaceSpotOrders(ctx context.Context, orders []ReplaceSpotOrderStruct) ([]CancelAndPlaceResp, error) { + if len(orders) == 0 { + return nil, errOrdersEmpty + } + req := map[string]any{ + "orderList": orders, + } + path := bitgetSpot + bitgetTrade + bitgetBatchCancelReplaceOrder + var resp struct { + Data []CancelAndPlaceResp `json:"data"` + } + return resp.Data, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodPost, path, nil, req, &resp) } // CancelSpotOrderByID cancels an order on the exchange -func (bi *Bitget) CancelSpotOrderByID(ctx context.Context, pair, clientOrderID string, orderID int64) (*OrderIDResp, error) { +func (bi *Bitget) CancelSpotOrderByID(ctx context.Context, pair, tpslType, clientOrderID string, orderID int64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -1159,7 +1237,8 @@ func (bi *Bitget) CancelSpotOrderByID(ctx context.Context, pair, clientOrderID s return nil, errOrderClientEmpty } req := map[string]any{ - "symbol": pair, + "symbol": pair, + "tpslType": tpslType, } if orderID != 0 { req["orderId"] = strconv.FormatInt(orderID, 10) @@ -1168,13 +1247,15 @@ func (bi *Bitget) CancelSpotOrderByID(ctx context.Context, pair, clientOrderID s req["clientOid"] = clientOrderID } path := bitgetSpot + bitgetTrade + bitgetCancelOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchPlaceSpotOrders places up to fifty orders on the exchange -func (bi *Bitget) BatchPlaceSpotOrders(ctx context.Context, pair string, orders []PlaceSpotOrderStruct, isCopyTradeLeader bool) (*BatchOrderResp, error) { - if pair == "" { +func (bi *Bitget) BatchPlaceSpotOrders(ctx context.Context, pair string, multiCurrencyMode, isCopyTradeLeader bool, orders []PlaceSpotOrderStruct) (*BatchOrderResp, error) { + if pair == "" && !multiCurrencyMode { return nil, errPairEmpty } if len(orders) == 0 { @@ -1184,6 +1265,9 @@ func (bi *Bitget) BatchPlaceSpotOrders(ctx context.Context, pair string, orders "symbol": pair, "orderList": orders, } + if multiCurrencyMode { + req["batchMode"] = "multiple" + } path := bitgetSpot + bitgetTrade + bitgetBatchOrders var resp struct { BatchOrderResp `json:"data"` @@ -1196,8 +1280,8 @@ func (bi *Bitget) BatchPlaceSpotOrders(ctx context.Context, pair string, orders } // BatchCancelOrders cancels up to fifty orders on the exchange -func (bi *Bitget) BatchCancelOrders(ctx context.Context, pair string, orderIDs []OrderIDStruct) (*BatchOrderResp, error) { - if pair == "" { +func (bi *Bitget) BatchCancelOrders(ctx context.Context, pair string, multiCurrencyMode bool, orderIDs []CancelSpotOrderStruct) (*BatchOrderResp, error) { + if pair == "" && !multiCurrencyMode { return nil, errPairEmpty } if len(orderIDs) == 0 { @@ -1207,6 +1291,9 @@ func (bi *Bitget) BatchCancelOrders(ctx context.Context, pair string, orderIDs [ "symbol": pair, "orderList": orderIDs, } + if multiCurrencyMode { + req["batchMode"] = "multiple" + } path := bitgetSpot + bitgetTrade + bitgetBatchCancel var resp *BatchOrderResp return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) @@ -1230,7 +1317,7 @@ func (bi *Bitget) CancelOrdersBySymbol(ctx context.Context, pair string) (string } // GetSpotOrderDetails returns information on a single order -func (bi *Bitget) GetSpotOrderDetails(ctx context.Context, orderID int64, clientOrderID string) ([]SpotOrderDetailData, error) { +func (bi *Bitget) GetSpotOrderDetails(ctx context.Context, orderID int64, clientOrderID string, acceptableDelay time.Duration) ([]SpotOrderDetailData, error) { if orderID == 0 && clientOrderID == "" { return nil, errOrderClientEmpty } @@ -1242,7 +1329,7 @@ func (bi *Bitget) GetSpotOrderDetails(ctx context.Context, orderID int64, client vals.Set("clientOid", clientOrderID) } vals.Set("requestTime", strconv.FormatInt(time.Now().UnixMilli(), 10)) - vals.Set("receiveWindow", "60000") + vals.Set("receiveWindow", strconv.FormatInt(acceptableDelay.Milliseconds(), 10)) path := bitgetSpot + bitgetTrade + bitgetOrderInfo return bi.spotOrderHelper(ctx, path, vals) } @@ -1317,7 +1404,7 @@ func (bi *Bitget) GetSpotFills(ctx context.Context, pair string, startTime, endT } // PlacePlanSpotOrder sets up an order to be placed after certain conditions are met -func (bi *Bitget) PlacePlanSpotOrder(ctx context.Context, pair, side, orderType, planType, triggerType, clientOrderID, strategy string, triggerPrice, executePrice, amount float64) (*OrderIDResp, error) { +func (bi *Bitget) PlacePlanSpotOrder(ctx context.Context, pair, side, orderType, planType, triggerType, clientOrderID, strategy string, triggerPrice, executePrice, amount float64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -1354,12 +1441,14 @@ func (bi *Bitget) PlacePlanSpotOrder(ctx context.Context, pair, side, orderType, req["clientOid"] = clientOrderID } path := bitgetSpot + bitgetTrade + bitgetPlacePlanOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate20, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate20, http.MethodPost, path, nil, req, &resp) } // ModifyPlanSpotOrder alters the price, trigger price, amount, or order type of a plan order -func (bi *Bitget) ModifyPlanSpotOrder(ctx context.Context, orderID int64, clientOrderID, orderType string, triggerPrice, executePrice, amount float64) (*OrderIDResp, error) { +func (bi *Bitget) ModifyPlanSpotOrder(ctx context.Context, orderID int64, clientOrderID, orderType string, triggerPrice, executePrice, amount float64) (*OrderIDStruct, error) { if orderID == 0 && clientOrderID == "" { return nil, errOrderClientEmpty } @@ -1388,8 +1477,10 @@ func (bi *Bitget) ModifyPlanSpotOrder(ctx context.Context, orderID int64, client req["orderId"] = strconv.FormatInt(orderID, 10) } path := bitgetSpot + bitgetTrade + bitgetModifyPlanOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate20, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate20, http.MethodPost, path, nil, req, &resp) } // CancelPlanSpotOrder cancels a plan order @@ -1652,7 +1743,7 @@ func (bi *Bitget) SubaccountTransfer(ctx context.Context, fromType, toType, curr } // WithdrawFunds withdraws funds from the user's account -func (bi *Bitget) WithdrawFunds(ctx context.Context, currency, transferType, address, chain, innerAddressType, areaCode, tag, note, clientOrderID string, amount float64) (*OrderIDResp, error) { +func (bi *Bitget) WithdrawFunds(ctx context.Context, currency, transferType, address, chain, innerAddressType, areaCode, tag, note, clientOrderID string, amount float64) (*OrderIDStruct, error) { if currency == "" { return nil, errCurrencyEmpty } @@ -1680,8 +1771,10 @@ func (bi *Bitget) WithdrawFunds(ctx context.Context, currency, transferType, add req["clientOid"] = clientOrderID } path := bitgetSpot + bitgetWallet + bitgetWithdrawal - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // GetSubaccountTransferRecord returns the user's sub-account transfer history @@ -2477,7 +2570,7 @@ func (bi *Bitget) GetHistoricalPositions(ctx context.Context, pair, productType } // PlaceFuturesOrder places a futures order on the exchange -func (bi *Bitget) PlaceFuturesOrder(ctx context.Context, pair, productType, marginMode, marginCoin, side, tradeSide, orderType, strategy, clientOID string, stopSurplusPrice, stopLossPrice, amount, price float64, reduceOnly, isCopyTradeLeader bool) (*OrderIDResp, error) { +func (bi *Bitget) PlaceFuturesOrder(ctx context.Context, pair, productType, marginMode, marginCoin, side, tradeSide, orderType, strategy, clientOID string, stopSurplusPrice, stopLossPrice, amount, price float64, reduceOnly, isCopyTradeLeader bool) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -2531,12 +2624,14 @@ func (bi *Bitget) PlaceFuturesOrder(ctx context.Context, pair, productType, marg if isCopyTradeLeader { rLim = Rate1 } - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) } // PlaceReversal attempts to close a position, in part or in whole, and opens a position of corresponding size on the opposite side. This operation may only be done in part under certain margin levels, market conditions, or other unspecified factors. If a reversal is attempted for an amount greater than the current outstanding position, that position will be closed, and a new position will be opened for the amount of the closed position; not the amount specified in the request. The side specified in the parameter should correspond to the side of the position you're attempting to close; if the original is open_long, use close_long; if the original is open_short, use close_short; if the original is sell_single, use buy_single. If the position is sell_single or buy_single, the amount parameter will be ignored, and the entire position will be closed, with a corresponding amount opened on the opposite side. -func (bi *Bitget) PlaceReversal(ctx context.Context, pair, marginCoin, productType, side, tradeSide, clientOID string, amount float64, isCopyTradeLeader bool) (*OrderIDResp, error) { +func (bi *Bitget) PlaceReversal(ctx context.Context, pair, marginCoin, productType, side, tradeSide, clientOID string, amount float64, isCopyTradeLeader bool) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -2566,8 +2661,10 @@ func (bi *Bitget) PlaceReversal(ctx context.Context, pair, marginCoin, productTy if isCopyTradeLeader { rLim = Rate1 } - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, rLim, http.MethodPost, path, nil, req, &resp) } // BatchPlaceFuturesOrders places multiple orders at once. Can also be used to modify the take-profit and stop-loss of an open position. @@ -2604,7 +2701,7 @@ func (bi *Bitget) BatchPlaceFuturesOrders(ctx context.Context, pair, productType } // ModifyFuturesOrder can change the size, price, take-profit, and stop-loss of an order. Size and price have to be modified at the same time, or the request will fail. If size and price are altered, the old order will be cancelled, and a new one will be created asynchronously. Due to the asynchronous creation of a new order, a new ClientOrderID must be supplied so it can be tracked. -func (bi *Bitget) ModifyFuturesOrder(ctx context.Context, orderID int64, clientOrderID, pair, productType, newClientOrderID string, newAmount, newPrice, newTakeProfit, newStopLoss float64) (*OrderIDResp, error) { +func (bi *Bitget) ModifyFuturesOrder(ctx context.Context, orderID int64, clientOrderID, pair, productType, newClientOrderID string, newAmount, newPrice, newTakeProfit, newStopLoss float64) (*OrderIDStruct, error) { if orderID == 0 && clientOrderID == "" { return nil, errOrderClientEmpty } @@ -2637,12 +2734,14 @@ func (bi *Bitget) ModifyFuturesOrder(ctx context.Context, orderID int64, clientO req["newPrice"] = strconv.FormatFloat(newPrice, 'f', -1, 64) } path := bitgetMix + bitgetOrder + bitgetModifyOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // CancelFuturesOrder cancels an order on the exchange -func (bi *Bitget) CancelFuturesOrder(ctx context.Context, pair, productType, marginCoin, clientOrderID string, orderID int64) (*OrderIDResp, error) { +func (bi *Bitget) CancelFuturesOrder(ctx context.Context, pair, productType, marginCoin, clientOrderID string, orderID int64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -2666,8 +2765,10 @@ func (bi *Bitget) CancelFuturesOrder(ctx context.Context, pair, productType, mar req["marginCoin"] = marginCoin } path := bitgetMix + bitgetOrder + bitgetCancelOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchCancelFuturesOrders cancels multiple orders at once @@ -2885,7 +2986,7 @@ func (bi *Bitget) GetFuturesTriggerOrderByID(ctx context.Context, planType, prod } // PlaceTPSLFuturesOrder places a take-profit or stop-loss futures order -func (bi *Bitget) PlaceTPSLFuturesOrder(ctx context.Context, marginCoin, productType, pair, planType, triggerType, holdSide, rangeRate, clientOrderID string, triggerPrice, executePrice, amount float64) (*OrderIDResp, error) { +func (bi *Bitget) PlaceTPSLFuturesOrder(ctx context.Context, marginCoin, productType, pair, planType, triggerType, holdSide, rangeRate, clientOrderID string, triggerPrice, executePrice, amount float64) (*OrderIDStruct, error) { if marginCoin == "" { return nil, errMarginCoinEmpty } @@ -2921,12 +3022,14 @@ func (bi *Bitget) PlaceTPSLFuturesOrder(ctx context.Context, marginCoin, product "size": strconv.FormatFloat(amount, 'f', -1, 64), } path := bitgetMix + bitgetOrder + bitgetPlaceTPSLOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // PlaceTriggerFuturesOrder places a trigger futures order -func (bi *Bitget) PlaceTriggerFuturesOrder(ctx context.Context, planType, pair, productType, marginMode, marginCoin, triggerType, side, tradeSide, orderType, clientOrderID, takeProfitTriggerType, stopLossTriggerType string, amount, executePrice, callbackRatio, triggerPrice, takeProfitTriggerPrice, takeProfitExecutePrice, stopLossTriggerPrice, stopLossExecutePrice float64, reduceOnly bool) (*OrderIDResp, error) { +func (bi *Bitget) PlaceTriggerFuturesOrder(ctx context.Context, planType, pair, productType, marginMode, marginCoin, triggerType, side, tradeSide, orderType, clientOrderID, takeProfitTriggerType, stopLossTriggerType string, amount, executePrice, callbackRatio, triggerPrice, takeProfitTriggerPrice, takeProfitExecutePrice, stopLossTriggerPrice, stopLossExecutePrice float64, reduceOnly bool) (*OrderIDStruct, error) { if planType == "" { return nil, errPlanTypeEmpty } @@ -2998,12 +3101,14 @@ func (bi *Bitget) PlaceTriggerFuturesOrder(ctx context.Context, planType, pair, req["stopLossTriggerType"] = stopLossTriggerType } path := bitgetMix + bitgetOrder + bitgetPlacePlanOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // ModifyTPSLFuturesOrder modifies a take-profit or stop-loss futures order -func (bi *Bitget) ModifyTPSLFuturesOrder(ctx context.Context, orderID int64, clientOrderID, marginCoin, productType, pair, triggerType string, triggerPrice, executePrice, amount, rangeRate float64) (*OrderIDResp, error) { +func (bi *Bitget) ModifyTPSLFuturesOrder(ctx context.Context, orderID int64, clientOrderID, marginCoin, productType, pair, triggerType string, triggerPrice, executePrice, amount, rangeRate float64) (*OrderIDStruct, error) { if orderID == 0 && clientOrderID == "" { return nil, errOrderClientEmpty } @@ -3037,12 +3142,14 @@ func (bi *Bitget) ModifyTPSLFuturesOrder(ctx context.Context, orderID int64, cli req["rangeRate"] = strconv.FormatFloat(rangeRate, 'f', -1, 64) } path := bitgetMix + bitgetOrder + bitgetModifyTPSLOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // ModifyTriggerFuturesOrder modifies a trigger futures order -func (bi *Bitget) ModifyTriggerFuturesOrder(ctx context.Context, orderID int64, clientOrderID, productType, triggerType, takeProfitTriggerType, stopLossTriggerType string, amount, executePrice, callbackRatio, triggerPrice, takeProfitTriggerPrice, takeProfitExecutePrice, stopLossTriggerPrice, stopLossExecutePrice float64) (*OrderIDResp, error) { +func (bi *Bitget) ModifyTriggerFuturesOrder(ctx context.Context, orderID int64, clientOrderID, productType, triggerType, takeProfitTriggerType, stopLossTriggerType string, amount, executePrice, callbackRatio, triggerPrice, takeProfitTriggerPrice, takeProfitExecutePrice, stopLossTriggerPrice, stopLossExecutePrice float64) (*OrderIDStruct, error) { if orderID == 0 && clientOrderID == "" { return nil, errOrderClientEmpty } @@ -3076,8 +3183,10 @@ func (bi *Bitget) ModifyTriggerFuturesOrder(ctx context.Context, orderID int64, req["newStopLossExecutePrice"] = strconv.FormatFloat(stopLossExecutePrice, 'f', -1, 64) } path := bitgetMix + bitgetOrder + bitgetModifyPlanOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // GetPendingTriggerFuturesOrders returns information on pending trigger orders @@ -3446,7 +3555,7 @@ func (bi *Bitget) GetCrossFlashRepayResult(ctx context.Context, idList []int64) } // PlaceCrossOrder places an order using cross margin -func (bi *Bitget) PlaceCrossOrder(ctx context.Context, pair, orderType, loanType, strategy, clientOrderID, side string, price, baseAmount, quoteAmount float64) (*OrderIDResp, error) { +func (bi *Bitget) PlaceCrossOrder(ctx context.Context, pair, orderType, loanType, strategy, clientOrderID, side string, price, baseAmount, quoteAmount float64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -3481,8 +3590,10 @@ func (bi *Bitget) PlaceCrossOrder(ctx context.Context, pair, orderType, loanType req["quoteSize"] = strconv.FormatFloat(quoteAmount, 'f', -1, 64) } path := bitgetMargin + bitgetCrossed + bitgetPlaceOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchPlaceCrossOrders places multiple orders using cross margin @@ -3503,7 +3614,7 @@ func (bi *Bitget) BatchPlaceCrossOrders(ctx context.Context, pair string, orders } // CancelCrossOrder cancels an order using cross margin -func (bi *Bitget) CancelCrossOrder(ctx context.Context, pair, clientOrderID string, orderID int64) (*OrderIDResp, error) { +func (bi *Bitget) CancelCrossOrder(ctx context.Context, pair, clientOrderID string, orderID int64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -3520,8 +3631,10 @@ func (bi *Bitget) CancelCrossOrder(ctx context.Context, pair, clientOrderID stri req["orderId"] = orderID } path := bitgetMargin + bitgetCrossed + bitgetCancelOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchCancelCrossOrders cancels multiple orders using cross margin @@ -3957,7 +4070,7 @@ func (bi *Bitget) GetIsolatedFlashRepayResult(ctx context.Context, idList []int6 } // PlaceIsolatedOrder places an order using isolated margin -func (bi *Bitget) PlaceIsolatedOrder(ctx context.Context, pair, orderType, loanType, strategy, clientOrderID, side string, price, baseAmount, quoteAmount float64) (*OrderIDResp, error) { +func (bi *Bitget) PlaceIsolatedOrder(ctx context.Context, pair, orderType, loanType, strategy, clientOrderID, side string, price, baseAmount, quoteAmount float64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -3992,8 +4105,10 @@ func (bi *Bitget) PlaceIsolatedOrder(ctx context.Context, pair, orderType, loanT req["quoteSize"] = strconv.FormatFloat(quoteAmount, 'f', -1, 64) } path := bitgetMargin + bitgetIsolated + bitgetPlaceOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchPlaceIsolatedOrders places multiple orders using isolated margin @@ -4014,7 +4129,7 @@ func (bi *Bitget) BatchPlaceIsolatedOrders(ctx context.Context, pair string, ord } // CancelIsolatedOrder cancels an order using isolated margin -func (bi *Bitget) CancelIsolatedOrder(ctx context.Context, pair, clientOrderID string, orderID int64) (*OrderIDResp, error) { +func (bi *Bitget) CancelIsolatedOrder(ctx context.Context, pair, clientOrderID string, orderID int64) (*OrderIDStruct, error) { if pair == "" { return nil, errPairEmpty } @@ -4031,8 +4146,10 @@ func (bi *Bitget) CancelIsolatedOrder(ctx context.Context, pair, clientOrderID s req["orderId"] = orderID } path := bitgetMargin + bitgetIsolated + bitgetCancelOrder - var resp *OrderIDResp - return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) + var resp struct { + OrderIDStruct `json:"data"` + } + return &resp.OrderIDStruct, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req, &resp) } // BatchCancelIsolatedOrders cancels multiple orders using isolated margin diff --git a/exchanges/bitget/bitget_test.go b/exchanges/bitget/bitget_test.go index e21f0f66d8c..916efa1dc78 100644 --- a/exchanges/bitget/bitget_test.go +++ b/exchanges/bitget/bitget_test.go @@ -45,7 +45,7 @@ const ( testAddress = "fake test address" // Test values used with live data, with the goal of never letting an order be executed testAmount = 0.001 - testPrice = 1e10 - 1 + testPrice = 1e10 - 3 // Test values used with demo functionality, with the goal of lining up with the relatively strict currency limits present there testAmount2 = 0.003 testPrice2 = 1667 @@ -207,10 +207,11 @@ func TestGetMerchantP2POrders(t *testing.T) { func TestGetMerchantAdvertisementList(t *testing.T) { t.Parallel() + bi.Verbose = true _, err := bi.GetMerchantAdvertisementList(context.Background(), time.Time{}, time.Time{}, 0, 0, 0, 0, "", "", "", "", "", "") assert.ErrorIs(t, err, common.ErrDateUnset) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) - _, err = bi.GetMerchantAdvertisementList(context.Background(), time.Now().Add(-time.Hour*24*7), time.Now(), 5, 1<<62, 0, 0, "", "", "", "", "", "") + _, err = bi.GetMerchantAdvertisementList(context.Background(), time.Now().Add(-time.Hour*24*7), time.Now(), 5, 1<<62, 0, 0, "", "sell", "USDT", "", "", "") assert.NoError(t, err) } @@ -237,35 +238,32 @@ func TestGetFuturesPositionRatios(t *testing.T) { assert.NotEmpty(t, resp) } -// Test for an endpoint which doesn't work -// func TestGetMarginPositionRatios(t *testing.T) { -// t.Parallel() -// _, err := bi.GetMarginPositionRatios(context.Background(), "", "", "") -// assert.ErrorIs(t, err, errPairEmpty) -// resp, err := bi.GetMarginPositionRatios(context.Background(), testPair.String(), "", "") -// require.NoError(t, err) -// assert.NotEmpty(t, resp.Data) -// } - -// Test for an endpoint which doesn't work -// func TestGetMarginLoanGrowth(t *testing.T) { -// t.Parallel() -// _, err := bi.GetMarginLoanGrowth(context.Background(), "", "", "") -// assert.ErrorIs(t, err, errPairEmpty) -// resp, err := bi.GetMarginLoanGrowth(context.Background(), testPair.String(), "", "") -// require.NoError(t, err) -// assert.NotEmpty(t, resp.Data) -// } - -// Test for an endpoint which doesn't work -// func TestGetIsolatedBorrowingRatio(t *testing.T) { -// t.Parallel() -// _, err := bi.GetIsolatedBorrowingRatio(context.Background(), "", "") -// assert.ErrorIs(t, err, errPairEmpty) -// resp, err := bi.GetIsolatedBorrowingRatio(context.Background(), testPair.String(), "") -// require.NoError(t, err) -// assert.NotEmpty(t, resp.Data) -// } +func TestGetMarginPositionRatios(t *testing.T) { + t.Parallel() + _, err := bi.GetMarginPositionRatios(context.Background(), "", "", "") + assert.ErrorIs(t, err, errPairEmpty) + resp, err := bi.GetMarginPositionRatios(context.Background(), testPair.String(), "", "") + require.NoError(t, err) + assert.NotEmpty(t, resp) +} + +func TestGetMarginLoanGrowth(t *testing.T) { + t.Parallel() + _, err := bi.GetMarginLoanGrowth(context.Background(), "", "", "") + assert.ErrorIs(t, err, errPairEmpty) + resp, err := bi.GetMarginLoanGrowth(context.Background(), testPair.String(), "", "") + require.NoError(t, err) + assert.NotEmpty(t, resp) +} + +func TestGetIsolatedBorrowingRatio(t *testing.T) { + t.Parallel() + _, err := bi.GetIsolatedBorrowingRatio(context.Background(), "", "") + assert.ErrorIs(t, err, errPairEmpty) + resp, err := bi.GetIsolatedBorrowingRatio(context.Background(), testPair.String(), "") + require.NoError(t, err) + assert.NotEmpty(t, resp) +} func TestGetFuturesRatios(t *testing.T) { t.Parallel() @@ -453,8 +451,7 @@ func TestGetBGBConvertCoins(t *testing.T) { func TestConvertBGB(t *testing.T) { t.Parallel() - // No matter what currency I use, this returns the error "currency does not support convert"; possibly a bad - // error message, with the true issue being lack of funds? + // No matter what currency I use, this returns the error "currency does not support convert"; possibly a bad error message, with the true issue being lack of funds? testGetOneArg(t, bi.ConvertBGB, nil, []string{testCrypto3.String()}, errCurrencyEmpty, false, true, canManipulateRealOrders) } @@ -469,7 +466,7 @@ func TestGetBGBConvertHistory(t *testing.T) { func TestGetCoinInfo(t *testing.T) { t.Parallel() - testGetOneArg(t, bi.GetCoinInfo, "", "", nil, true, false, false) + testGetOneArg(t, bi.GetCoinInfo, "", testCrypto.String(), nil, true, false, false) } func TestGetSymbolInfo(t *testing.T) { @@ -542,28 +539,71 @@ func TestGetSpotMarketTrades(t *testing.T) { func TestPlaceSpotOrder(t *testing.T) { t.Parallel() - _, err := bi.PlaceSpotOrder(context.Background(), "", "", "", "", "", 0, 0, false) + _, err := bi.PlaceSpotOrder(context.Background(), "", "", "", "", "", "", 0, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errPairEmpty) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "", "", "", "", 0, 0, false) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "", "", "", "", "", 0, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errSideEmpty) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "", "", "", 0, 0, false) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "", "", "", "", 0, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errOrderTypeEmpty) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "", "", 0, 0, false) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "", "", "", 0, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errStrategyEmpty) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", 0, 0, false) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", "", 0, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errLimitPriceEmpty) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", testPrice, 0, false) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", "", testPrice, 0, 0, 0, 0, 0, 0, false, 0) assert.ErrorIs(t, err, errAmountEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) - _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", testPrice, testAmount, true) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", "", testPrice, testAmount, 0, testPrice-1, testPrice-2, testPrice+1, testPrice+2, true, 0) + assert.NoError(t, err) + _, err = bi.PlaceSpotOrder(context.Background(), testPair.String(), "sell", "limit", "IOC", "", "", testPrice, testAmount, testPrice/10, 0, 0, 0, 0, false, time.Minute) + assert.NoError(t, err) +} + +func TestCancelAndPlaceSpotOrder(t *testing.T) { + t.Parallel() + _, err := bi.CancelAndPlaceSpotOrder(context.Background(), "", "", "", 0, 0, 0, 0, 0, 0, 0) + assert.ErrorIs(t, err, errPairEmpty) + _, err = bi.CancelAndPlaceSpotOrder(context.Background(), testPair.String(), "", "", 0, 0, 0, 0, 0, 0, 0) + assert.ErrorIs(t, err, errOrderClientEmpty) + _, err = bi.CancelAndPlaceSpotOrder(context.Background(), testPair.String(), "meow", "", 0, 0, 0, 0, 0, 0, 0) + assert.ErrorIs(t, err, errPriceEmpty) + sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) + resp, err := bi.GetUnfilledOrders(context.Background(), testPair.String(), time.Time{}, time.Time{}, 5, 1<<62, 0) + require.NoError(t, err) + if len(resp) == 0 { + t.Skip(skipInsufficientOrders) + } + cID := clientIDGenerator() + _, err = bi.CancelAndPlaceSpotOrder(context.Background(), testPair.String(), resp[0].ClientOrderID, cID, testPrice, testAmount, testPrice-1, testPrice-2, testPrice+1, testPrice+2, int64(resp[0].OrderID)) + assert.NoError(t, err) +} + +func TestBatchCancelAndPlaceSpotOrders(t *testing.T) { + t.Parallel() + var req []ReplaceSpotOrderStruct + _, err := bi.BatchCancelAndPlaceSpotOrders(context.Background(), req) + assert.ErrorIs(t, err, errOrdersEmpty) + sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) + resp, err := bi.GetUnfilledOrders(context.Background(), testPair.String(), time.Time{}, time.Time{}, 5, 1<<62, 0) + require.NoError(t, err) + if len(resp) == 0 { + t.Skip(skipInsufficientOrders) + } + req = append(req, ReplaceSpotOrderStruct{ + OrderID: int64(resp[0].OrderID), + OldClientOrderID: resp[0].ClientOrderID, + Price: testPrice, + Amount: testAmount, + Pair: resp[0].Symbol, + }) + _, err = bi.BatchCancelAndPlaceSpotOrders(context.Background(), req) assert.NoError(t, err) } func TestCancelSpotOrderByID(t *testing.T) { t.Parallel() - _, err := bi.CancelSpotOrderByID(context.Background(), "", "", 0) + _, err := bi.CancelSpotOrderByID(context.Background(), "", "", "", 0) assert.ErrorIs(t, err, errPairEmpty) - _, err = bi.CancelSpotOrderByID(context.Background(), testPair.String(), "", 0) + _, err = bi.CancelSpotOrderByID(context.Background(), testPair.String(), "", "", 0) assert.ErrorIs(t, err, errOrderClientEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.GetUnfilledOrders(context.Background(), testPair.String(), time.Time{}, time.Time{}, 5, 1<<62, 0) @@ -571,16 +611,16 @@ func TestCancelSpotOrderByID(t *testing.T) { if len(resp) == 0 { t.Skip(skipInsufficientOrders) } - _, err = bi.CancelSpotOrderByID(context.Background(), testPair.String(), resp[0].ClientOrderID, int64(resp[0].OrderID)) + _, err = bi.CancelSpotOrderByID(context.Background(), testPair.String(), "", resp[0].ClientOrderID, int64(resp[0].OrderID)) assert.NoError(t, err) } func TestBatchPlaceSpotOrders(t *testing.T) { t.Parallel() var req []PlaceSpotOrderStruct - _, err := bi.BatchPlaceSpotOrders(context.Background(), "", req, false) + _, err := bi.BatchPlaceSpotOrders(context.Background(), "", false, false, req) assert.ErrorIs(t, err, errPairEmpty) - _, err = bi.BatchPlaceSpotOrders(context.Background(), "meow", req, false) + _, err = bi.BatchPlaceSpotOrders(context.Background(), "meow", false, false, req) assert.ErrorIs(t, err, errOrdersEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) req = append(req, PlaceSpotOrderStruct{ @@ -589,18 +629,19 @@ func TestBatchPlaceSpotOrders(t *testing.T) { Strategy: "IOC", Price: testPrice, Size: testAmount, + Pair: testPair.String(), }) - resp, err := bi.BatchPlaceSpotOrders(context.Background(), testPair.String(), req, true) + resp, err := bi.BatchPlaceSpotOrders(context.Background(), testPair.String(), true, true, req) require.NoError(t, err) assert.NotEmpty(t, resp) } func TestBatchCancelOrders(t *testing.T) { t.Parallel() - var req []OrderIDStruct - _, err := bi.BatchCancelOrders(context.Background(), "", req) + var req []CancelSpotOrderStruct + _, err := bi.BatchCancelOrders(context.Background(), "", false, req) assert.ErrorIs(t, err, errPairEmpty) - _, err = bi.BatchCancelOrders(context.Background(), "meow", req) + _, err = bi.BatchCancelOrders(context.Background(), "meow", false, req) assert.ErrorIs(t, err, errOrderIDEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.GetUnfilledOrders(context.Background(), testPair.String(), time.Time{}, time.Time{}, 5, 1<<62, 0) @@ -608,11 +649,12 @@ func TestBatchCancelOrders(t *testing.T) { if len(resp) == 0 { t.Skip(skipInsufficientOrders) } - req = append(req, OrderIDStruct{ - OrderID: resp[0].OrderID, + req = append(req, CancelSpotOrderStruct{ + OrderID: int64(resp[0].OrderID), ClientOrderID: resp[0].ClientOrderID, + Pair: resp[0].Symbol, }) - resp2, err := bi.BatchCancelOrders(context.Background(), testPair.String(), req) + resp2, err := bi.BatchCancelOrders(context.Background(), testPair.String(), true, req) assert.NoError(t, err) assert.NotEmpty(t, resp2) } @@ -624,11 +666,11 @@ func TestCancelOrderBySymbol(t *testing.T) { func TestGetSpotOrderDetails(t *testing.T) { t.Parallel() - _, err := bi.GetSpotOrderDetails(context.Background(), 0, "") + _, err := bi.GetSpotOrderDetails(context.Background(), 0, "", 0) assert.ErrorIs(t, err, errOrderClientEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, bi) ordIDs := getPlanOrdIDHelper(t, false) - _, err = bi.GetSpotOrderDetails(context.Background(), int64(ordIDs.OrderID), ordIDs.ClientOrderID) + _, err = bi.GetSpotOrderDetails(context.Background(), int64(ordIDs.OrderID), ordIDs.ClientOrderID, time.Minute) assert.NoError(t, err) } @@ -680,7 +722,7 @@ func TestPlacePlanSpotOrder(t *testing.T) { sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.PlacePlanSpotOrder(context.Background(), testPair.String(), "sell", "limit", "", "fill_price", clientIDGenerator(), "ioc", testPrice, testPrice, testAmount) require.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestGetCurrentSpotPlanOrders(t *testing.T) { @@ -1227,7 +1269,7 @@ func TestPlaceFuturesOrder(t *testing.T) { sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.PlaceFuturesOrder(context.Background(), testPair2.String(), testFiat2.String()+"-FUTURES", "isolated", testFiat2.String(), "buy", "open", "limit", "GTC", clientIDGenerator(), testPrice2+1, testPrice2-1, testAmount2, testPrice2, true, true) assert.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestPlaceReversal(t *testing.T) { @@ -1383,7 +1425,7 @@ func TestPlaceTPSLFuturesOrder(t *testing.T) { sharedtestvalues.SkipTestIfCredentialsUnset(t, bi, canManipulateRealOrders) resp, err := bi.PlaceTPSLFuturesOrder(context.Background(), testFiat2.String(), testFiat2.String()+"-FUTURES", testPair2.String(), "profit_plan", "", "short", "", "", testPrice2+2, 0, testAmount2) require.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestPlaceTriggerFuturesOrder(t *testing.T) { @@ -1420,7 +1462,7 @@ func TestPlaceTriggerFuturesOrder(t *testing.T) { // that parameter with various values, or to tweak other parameters, yielded no difference resp, err := bi.PlaceTriggerFuturesOrder(context.Background(), "normal_plan", testPair2.String(), testFiat2.String()+"-FUTURES", "isolated", testFiat2.String(), "mark_price", "Sell", "", "limit", clientIDGenerator(), "", "", testAmount2*1000, testPrice2+2, 0, testPrice2+1, 0, 0, 0, 0, false) require.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestModifyTPSLFuturesOrder(t *testing.T) { @@ -1441,7 +1483,7 @@ func TestModifyTPSLFuturesOrder(t *testing.T) { ID := getTrigOrdIDHelper(t, []string{"profit_loss"}) resp, err := bi.ModifyTPSLFuturesOrder(context.Background(), int64(ID.OrderID), ID.ClientOrderID, testFiat2.String(), testFiat2.String()+"-FUTURES", testPair2.String(), "", testPrice2-1, testPrice2+2, testAmount2, 0.1) require.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestModifyTriggerFuturesOrder(t *testing.T) { @@ -2826,7 +2868,7 @@ func TestModifyPlanSpotOrder(t *testing.T) { } resp, err := bi.ModifyPlanSpotOrder(context.Background(), ordID.OrderList[0].OrderID, ordID.OrderList[0].ClientOrderID, "limit", testPrice, testPrice, testAmount) require.NoError(t, err) - assert.NotEmpty(t, resp.Data) + assert.NotEmpty(t, resp) } func TestCancelPlanSpotOrder(t *testing.T) { @@ -3336,23 +3378,17 @@ func aBenchmarkHelper(a, pag int64) { } // irrelevant/outdated data retained for formatting -// 4952 292175 ns/op 165455 B/op 4 allocs/op +// 139 9192238 ns/op 271904 B/op 2038 allocs/op func BenchmarkGen(b *testing.B) { - for i := 0; i < b.N; i++ { - var sizableArray [][10]int - var done bool - for !done { - // tempArray := make([][10]int, 100) - // for x := 0; x < 100; x++ { - // tempArray[x] = [10]int{5, 1 << 30, i % 27, x % 9, x ^ i, 2, 3, 4, 5, 6} - // } - // sizableArray = append(sizableArray, tempArray...) - for x := 0; x < 100; x++ { - sizableArray = append(sizableArray, [10]int{5, 1 << 30, i % 27, x % 9, x ^ i, 2, 3, 4, 5, 6}) - } - if i%5 == 0 || len(sizableArray) > 1000 { - done = true - } - } - } + // var resp WsResponse + // err := json.Unmarshal(data, &resp) + // if err != nil { + // b.Fatal(err) + // } + // for i := 0; i < b.N; i++ { + // err = bi.orderbookDataHandler(resp) + // if err != nil { + // b.Fatal(err) + // } + // } } diff --git a/exchanges/bitget/bitget_types.go b/exchanges/bitget/bitget_types.go index f839f188a9f..e4f606f397d 100644 --- a/exchanges/bitget/bitget_types.go +++ b/exchanges/bitget/bitget_types.go @@ -207,8 +207,8 @@ type MerchantCertifiedList struct { Desc string `json:"desc"` } -// P2PAdListResp holds information on P2P advertisements -type P2PAdListResp struct { +// AdvertisementList is a sub-struct holding information on P2P advertisements +type AdvertisementList struct { AdID int64 `json:"adId,string"` AdvNum int64 `json:"advNo,string"` Side string `json:"side"` @@ -235,6 +235,12 @@ type P2PAdListResp struct { MerchantCertifiedList []MerchantCertifiedList `json:"merchantCertifiedList"` } +// P2PAdListResp holds information on P2P advertisements +type P2PAdListResp struct { + AdvertisementList []AdvertisementList `json:"advList"` + MinAdvertisementID int64 `json:"minAdvId,string"` +} + // WhaleNetFlowResp holds information on whale trading volumes type WhaleNetFlowResp struct { Volume float64 `json:"volume,string"` @@ -256,32 +262,23 @@ type PosRatFutureResp struct { Timestamp UnixTimestamp `json:"ts"` } -// Type for an endpoint which doesn't work -// // PosRatMarginResp holds information on position ratios in margin trading -// type PosRatMarginResp struct { -// Data []struct { -// Timestamp UnixTimestamp `json:"ts"` -// LongShortRatio float64 `json:"longShortRatio,string"` -// } `json:"data"` -// } - -// Type for an endpoint which doesn't work -// // LoanGrowthResp holds information on loan growth -// type LoanGrowthResp struct { -// Data []struct { -// Timestamp UnixTimestamp `json:"ts"` -// GrowthRate float64 `json:"growthRate,string"` -// } `json:"data"` -// } - -// Type for an endpoint which doesn't work -// // BorrowRatioResp holds information on borrowing ratios -// type BorrowRatioResp struct { -// Data []struct { -// Timestamp UnixTimestamp `json:"ts"` -// BorrowRate float64 `json:"borrowRate,string"` -// } `json:"data"` -// } +// PosRatMarginResp holds information on position ratios in margin trading +type PosRatMarginResp struct { + Timestamp UnixTimestamp `json:"ts"` + LongShortRatio float64 `json:"longShortRatio,string"` +} + +// LoanGrowthResp holds information on loan growth +type LoanGrowthResp struct { + Timestamp UnixTimestamp `json:"ts"` + GrowthRate float64 `json:"growthRate,string"` +} + +// BorrowRatioResp holds information on borrowing ratios +type BorrowRatioResp struct { + Timestamp UnixTimestamp `json:"ts"` + BorrowRate float64 `json:"borrowRate,string"` +} // RatioResp holds information on ratios type RatioResp struct { @@ -514,19 +511,21 @@ type BGBConvHistResp struct { // ChainInfo is a sub-struct containing information on supported chains for a currency type ChainInfo struct { - Chain string `json:"chain"` - NeedTag bool `json:"needTag,string"` - Withdrawable bool `json:"withdrawable,string"` - Rechargeable bool `json:"rechargeable,string"` - WithdrawFee float64 `json:"withdrawFee,string"` - ExtraWithdrawFee float64 `json:"extraWithdrawFee,string"` - DepositConfirm uint16 `json:"depositConfirm,string"` - WithdrawConfirm uint16 `json:"withdrawConfirm,string"` - MinDepositAmount float64 `json:"minDepositAmount,string"` - MinWithdrawAmount float64 `json:"minWithdrawAmount,string"` - BrowserURL string `json:"browserUrl"` - ContractAddress string `json:"contractAddress"` - WithdrawStep uint8 `json:"withdrawStep,string"` + Chain string `json:"chain"` + NeedTag bool `json:"needTag,string"` + Withdrawable bool `json:"withdrawable,string"` + Rechargeable bool `json:"rechargeable,string"` + WithdrawFee float64 `json:"withdrawFee,string"` + ExtraWithdrawFee float64 `json:"extraWithdrawFee,string"` + DepositConfirm uint16 `json:"depositConfirm,string"` + WithdrawConfirm uint16 `json:"withdrawConfirm,string"` + MinDepositAmount float64 `json:"minDepositAmount,string"` + MinWithdrawAmount float64 `json:"minWithdrawAmount,string"` + BrowserURL string `json:"browserUrl"` + ContractAddress string `json:"contractAddress"` + WithdrawStep uint8 `json:"withdrawStep,string"` + WithdrawMinimumScale uint8 `json:"withdrawMinimumScale,string"` + Congestion string `json:"congestion"` } // CoinInfoResp contains information on supported spot currencies @@ -650,12 +649,25 @@ type MarketFillsResp struct { // PlaceOrderStruct contains information on an order to be placed type PlaceSpotOrderStruct struct { - Side string `json:"side"` - OrderType string `json:"orderType"` - Strategy string `json:"force"` - Price float64 `json:"price,string"` - Size float64 `json:"size,string"` - ClientOrderID string `json:"clientOId"` + Pair string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"orderType"` + Strategy string `json:"force"` + Price float64 `json:"price,string"` + Size float64 `json:"size,string"` + ClientOrderID string `json:"clientOId,omitempty"` + STPMode string `json:"stpMode"` + PresetTakeProfitPrice float64 `json:"presetTakeProfitPrice,string,omitempty"` + ExecuteTakeProfitPrice float64 `json:"executeTakeProfitPrice,string,omitempty"` + PresetStopLossPrice float64 `json:"presetStopLossPrice,string,omitempty"` + ExecuteStopLossPrice float64 `json:"executeStopLossPrice,string,omitempty"` +} + +// CancelSpotOrderStruct contains information on an order to be cancelled +type CancelSpotOrderStruct struct { + Pair string `json:"symbol"` + OrderID int64 `json:"orderId,string,omitempty"` + ClientOrderID string `json:"clientOId,omitempty"` } // EmptyInt is a type used to unmarshal empty string into 0, and numbers encoded as strings into int64 @@ -783,9 +795,26 @@ type SpotFillsResp struct { UpdateTime UnixTimestamp `json:"uTime"` } -// OrderIDResp contains order IDs in the format returned by the exchange -type OrderIDResp struct { - Data OrderIDStruct `json:"data"` +// CancelAndPlaceResp contains information on the success or failure of a replaced order +type CancelAndPlaceResp struct { + OrderID EmptyInt `json:"orderId"` + ClientOrderID string `json:"clientOid"` + Success SuccessBool `json:"success"` + Message string `json:"msg"` +} + +// ReplaceSpotOrderStruct contains information on an order to be replaced +type ReplaceSpotOrderStruct struct { + Pair string `json:"symbol"` + Price float64 `json:"price,string"` + Amount float64 `json:"size,string"` + OldClientOrderID string `json:"clientOid,omitempty"` + OrderID int64 `json:"orderId,string,omitempty"` + NewClientOrderID string `json:"newClientOid,omitempty"` + PresetTakeProfitPrice float64 `json:"presetTakeProfitPrice,string,omitempty"` + ExecuteTakeProfitPrice float64 `json:"executeTakeProfitPrice,string,omitempty"` + PresetStopLossPrice float64 `json:"presetStopLossPrice,string,omitempty"` + ExecuteStopLossPrice float64 `json:"executeStopLossPrice,string,omitempty"` } // PlanSpotOrder is a sub-struct that contains information on a planned order diff --git a/exchanges/bitget/bitget_websocket.go b/exchanges/bitget/bitget_websocket.go index 166cfe2c05d..536c726e5b7 100644 --- a/exchanges/bitget/bitget_websocket.go +++ b/exchanges/bitget/bitget_websocket.go @@ -76,13 +76,13 @@ var defaultSubscriptions = subscription.List{ {Enabled: false, Channel: subscription.CandlesChannel, Asset: asset.Futures}, {Enabled: false, Channel: subscription.AllOrdersChannel, Asset: asset.Spot}, {Enabled: false, Channel: subscription.AllOrdersChannel, Asset: asset.Futures}, - {Enabled: false, Channel: subscription.OrderbookChannel, Asset: asset.Spot}, + {Enabled: true, Channel: subscription.OrderbookChannel, Asset: asset.Spot}, {Enabled: false, Channel: subscription.OrderbookChannel, Asset: asset.Futures}, {Enabled: false, Channel: subscription.MyTradesChannel, Authenticated: true, Asset: asset.Spot}, {Enabled: false, Channel: subscription.MyTradesChannel, Authenticated: true, Asset: asset.Futures}, {Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Spot}, {Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Futures}, - {Enabled: true, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Margin}, + {Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.Margin}, {Enabled: false, Channel: subscription.MyOrdersChannel, Authenticated: true, Asset: asset.CrossMargin}, {Enabled: false, Channel: "myTriggerOrders", Authenticated: true, Asset: asset.Spot}, {Enabled: false, Channel: "myTriggerOrders", Authenticated: true, Asset: asset.Futures}, @@ -945,9 +945,12 @@ func (bi *Bitget) orderbookDataHandler(wsResponse WsResponse) error { Asset: itemDecoder(wsResponse.Arg.InstrumentType), Checksum: uint32(ob[0].Checksum), } - err = bi.Websocket.Orderbook.Update(&update) - if err != nil { - return err + // Sometimes the exchange returns updates with no new asks or bids, just a checksum and timestamp + if len(update.Bids) != 0 || len(update.Asks) != 0 { + err = bi.Websocket.Orderbook.Update(&update) + if err != nil { + return err + } } } return nil diff --git a/exchanges/bitget/bitget_wrapper.go b/exchanges/bitget/bitget_wrapper.go index c57624cc71c..dc75ef2d8e7 100644 --- a/exchanges/bitget/bitget_wrapper.go +++ b/exchanges/bitget/bitget_wrapper.go @@ -741,7 +741,7 @@ func (bi *Bitget) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm if err != nil { return nil, err } - var IDs *OrderIDResp + var IDs *OrderIDStruct strat, err := strategyTruthTable(s.ImmediateOrCancel, s.FillOrKill, s.PostOnly) if err != nil { return nil, err @@ -752,7 +752,7 @@ func (bi *Bitget) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm } switch s.AssetType { case asset.Spot: - IDs, err = bi.PlaceSpotOrder(ctx, s.Pair.String(), s.Side.String(), s.Type.Lower(), strat, cID.String(), s.Price, s.Amount, false) + IDs, err = bi.PlaceSpotOrder(ctx, s.Pair.String(), s.Side.String(), s.Type.Lower(), strat, cID.String(), "", s.Price, s.Amount, s.TriggerPrice, 0, 0, 0, 0, false, 0) case asset.Futures: IDs, err = bi.PlaceFuturesOrder(ctx, s.Pair.String(), getProductType(s.Pair), marginStringer(s.MarginType), s.Pair.Quote.String(), sideEncoder(s.Side, false), "", s.Type.Lower(), strat, cID.String(), 0, 0, s.Amount, s.Price, s.ReduceOnly, false) case asset.Margin, asset.CrossMargin: @@ -771,11 +771,11 @@ func (bi *Bitget) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm if err != nil { return nil, err } - resp, err := s.DeriveSubmitResponse(strconv.FormatInt(int64(IDs.Data.OrderID), 10)) + resp, err := s.DeriveSubmitResponse(strconv.FormatInt(int64(IDs.OrderID), 10)) if err != nil { return nil, err } - resp.ClientOrderID = IDs.Data.ClientOrderID + resp.ClientOrderID = IDs.ClientOrderID return resp, nil } @@ -786,7 +786,7 @@ func (bi *Bitget) ModifyOrder(ctx context.Context, action *order.Modify) (*order if err != nil { return nil, err } - var IDs *OrderIDResp + var IDs *OrderIDStruct originalID, err := strconv.ParseInt(action.OrderID, 10, 64) if err != nil { return nil, err @@ -812,8 +812,8 @@ func (bi *Bitget) ModifyOrder(ctx context.Context, action *order.Modify) (*order if err != nil { return nil, err } - resp.OrderID = strconv.FormatInt(int64(IDs.Data.OrderID), 10) - resp.ClientOrderID = IDs.Data.ClientOrderID + resp.OrderID = strconv.FormatInt(int64(IDs.OrderID), 10) + resp.ClientOrderID = IDs.ClientOrderID return resp, nil } @@ -829,7 +829,7 @@ func (bi *Bitget) CancelOrder(ctx context.Context, ord *order.Cancel) error { } switch ord.AssetType { case asset.Spot: - _, err = bi.CancelSpotOrderByID(ctx, ord.Pair.String(), ord.ClientOrderID, originalID) + _, err = bi.CancelSpotOrderByID(ctx, ord.Pair.String(), ord.ClientOrderID, "", originalID) case asset.Futures: _, err = bi.CancelFuturesOrder(ctx, ord.Pair.String(), getProductType(ord.Pair), ord.Pair.Quote.String(), ord.ClientOrderID, originalID) case asset.Margin: @@ -862,7 +862,15 @@ func (bi *Bitget) CancelBatchOrders(ctx context.Context, orders []order.Cancel) for pair, batch := range batchByPair { switch assetType { case asset.Spot: - status, err = bi.BatchCancelOrders(ctx, pair.String(), batch) + // This no longer needs to be batched by pair, refactor if many others get similar changes + batchConv := make([]CancelSpotOrderStruct, len(batch)) + for i := range batch { + batchConv[i] = CancelSpotOrderStruct{ + OrderID: int64(batch[i].OrderID), + ClientOrderID: batch[i].ClientOrderID, + } + } + status, err = bi.BatchCancelOrders(ctx, pair.String(), false, batchConv) case asset.Futures: status, err = bi.BatchCancelFuturesOrders(ctx, batch, pair.String(), getProductType(pair), pair.Quote.String()) case asset.Margin: @@ -929,7 +937,7 @@ func (bi *Bitget) GetOrderInfo(ctx context.Context, orderID string, pair currenc } switch assetType { case asset.Spot: - ordInfo, err := bi.GetSpotOrderDetails(ctx, ordID, "") + ordInfo, err := bi.GetSpotOrderDetails(ctx, ordID, "", time.Minute) if err != nil { return nil, err } @@ -1092,7 +1100,7 @@ func (bi *Bitget) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawReque return nil, err } ret := &withdraw.ExchangeResponse{ - ID: strconv.FormatInt(int64(resp.Data.OrderID), 10), + ID: strconv.FormatInt(int64(resp.OrderID), 10), } return ret, nil }