exchanges/order, GateIO: Update USDT margined futures pathway for cancel all orders (#2021)

* order/gateio: update USDT margined futures pathway for cancel all orders and drop count field

* gateio: add and expand tests for CancelAllOrders and getExchangeSide

* Add test for load

* linter: fix

* Update exchanges/kraken/kraken_wrapper.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* Update exchanges/order/orders.go

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* gk: nits

* glorious: nits

* reverted change for options

* Update exchanges/gateio/gateio_wrapper_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/gateio/gateio_wrapper.go

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* Update exchanges/deribit/deribit_wrapper.go

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* Update exchanges/gateio/gateio_wrapper_test.go

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* Update exchanges/gateio/gateio_wrapper_test.go

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>

* Add consts for cancel side references

* Update exchanges/gateio/gateio_wrapper.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_wrapper.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* Update exchanges/gateio/gateio_websocket_request_futures.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* thrasher-: nits

---------

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2025-10-02 15:50:51 +10:00
committed by GitHub
parent e11765bc36
commit dcf98ec700
13 changed files with 204 additions and 156 deletions

View File

@@ -1438,12 +1438,14 @@ func (s *RPCServer) CancelAllOrders(ctx context.Context, r *gctrpc.CancelAllOrde
// TODO: Change to order manager
resp, err := exch.CancelAllOrders(ctx, nil)
if err != nil {
return &gctrpc.CancelAllOrdersResponse{}, err
return nil, err
}
return &gctrpc.CancelAllOrdersResponse{
Count: resp.Count, // count of deleted orders
}, nil
cancelledOrders := new(gctrpc.Orders)
cancelledOrders.Exchange = r.Exchange
cancelledOrders.OrderStatus = resp.Status
return &gctrpc.CancelAllOrdersResponse{Orders: []*gctrpc.Orders{cancelledOrders}, Count: int64(len(resp.Status))}, nil
}
// ModifyOrder modifies an existing order if it exists

View File

@@ -693,17 +693,18 @@ func (e *Exchange) CancelBatchOrders(_ context.Context, _ []order.Cancel) (*orde
}
// CancelAllOrders cancels all orders associated with a currency pair
func (e *Exchange) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
if err := orderCancellation.Validate(); err != nil {
return order.CancelAllResponse{}, err
func (e *Exchange) CancelAllOrders(ctx context.Context, cancel *order.Cancel) (order.CancelAllResponse, error) {
var resp order.CancelAllResponse
if err := cancel.Validate(); err != nil {
return resp, err
}
var cancelData *MultipleCancelResponse
pairFmt, err := e.GetPairFormat(orderCancellation.AssetType, true)
fPair, err := e.FormatExchangeCurrency(cancel.Pair, cancel.AssetType)
if err != nil {
return order.CancelAllResponse{}, err
return resp, err
}
var orderTypeStr string
switch orderCancellation.Type {
switch cancel.Type {
case order.Limit:
orderTypeStr = order.Limit.String()
case order.Market:
@@ -711,26 +712,24 @@ func (e *Exchange) CancelAllOrders(ctx context.Context, orderCancellation *order
case order.AnyType, order.UnknownType:
orderTypeStr = "all"
default:
return order.CancelAllResponse{}, fmt.Errorf("%s: orderType %v is not valid", e.Name, orderCancellation.Type)
return resp, fmt.Errorf("%s %w: %v", e.Name, order.ErrTypeIsInvalid, cancel.Type)
}
var cancelData *MultipleCancelResponse
if e.Websocket.IsConnected() && e.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
cancelData, err = e.WSSubmitCancelAllByInstrument(ctx, pairFmt.Format(orderCancellation.Pair), orderTypeStr, true, true)
cancelData, err = e.WSSubmitCancelAllByInstrument(ctx, fPair.String(), orderTypeStr, true, true)
} else {
cancelData, err = e.SubmitCancelAllByInstrument(ctx, pairFmt.Format(orderCancellation.Pair), orderTypeStr, true, true)
cancelData, err = e.SubmitCancelAllByInstrument(ctx, fPair.String(), orderTypeStr, true, true)
}
if err != nil {
return order.CancelAllResponse{}, err
return resp, err
}
response := order.CancelAllResponse{Count: cancelData.CancelCount}
if len(cancelData.CancelDetails) > 0 {
response.Status = make(map[string]string)
for a := range cancelData.CancelDetails {
for b := range cancelData.CancelDetails[a].Result {
response.Status[cancelData.CancelDetails[a].Result[b].OrderID] = cancelData.CancelDetails[a].Result[b].OrderState
}
for a := range cancelData.CancelDetails {
for b := range cancelData.CancelDetails[a].Result {
resp.Add(cancelData.CancelDetails[a].Result[b].OrderID, cancelData.CancelDetails[a].Result[b].OrderState)
}
}
return response, nil
return resp, nil
}
// GetOrderInfo returns order information based on order ID

View File

@@ -2820,7 +2820,7 @@ func (e *Exchange) CancelMultipleDeliveryOrders(ctx context.Context, contract cu
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
}
params := url.Values{}
if side == "ask" || side == "bid" {
if side == order.Ask.Lower() || side == order.Bid.Lower() {
params.Set("side", side)
}
params.Set("contract", contract.String())

View File

@@ -66,29 +66,6 @@ func TestUpdateTradablePairs(t *testing.T) {
testexch.UpdatePairsOnce(t, e)
}
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
_, err := e.CancelAllOrders(t.Context(), nil)
require.ErrorIs(t, err, order.ErrCancelOrderIsNil)
r := &order.Cancel{
OrderID: "1",
AccountID: "1",
}
for _, a := range e.GetAssetTypes(false) {
r.AssetType = a
r.Pair = currency.EMPTYPAIR
_, err = e.CancelAllOrders(t.Context(), r)
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
r.Pair = getPair(t, a)
_, err = e.CancelAllOrders(t.Context(), r)
require.NoError(t, err)
}
}
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)

View File

@@ -80,7 +80,7 @@ func (e *Exchange) WebsocketFuturesCancelAllOpenFuturesOrders(ctx context.Contex
return nil, err
}
if side != "" && side != "ask" && side != "bid" {
if side != "" && side != order.Ask.Lower() && side != order.Bid.Lower() {
return nil, fmt.Errorf("%w: %s", order.ErrSideIsInvalid, side)
}

View File

@@ -31,10 +31,10 @@ func TestWebsocketFuturesSubmitOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
out.AutoSize = ""
got, err := g.WebsocketFuturesSubmitOrder(t.Context(), asset.USDTMarginedFutures, out)
got, err := e.WebsocketFuturesSubmitOrder(t.Context(), asset.USDTMarginedFutures, out)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -73,15 +73,15 @@ func TestWebsocketFuturesSubmitOrders(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
// test single order
got, err := g.WebsocketFuturesSubmitOrders(t.Context(), asset.CoinMarginedFutures, out)
got, err := e.WebsocketFuturesSubmitOrders(t.Context(), asset.CoinMarginedFutures, out)
require.NoError(t, err)
require.NotEmpty(t, got)
// test batch orders
got, err = g.WebsocketFuturesSubmitOrders(t.Context(), asset.CoinMarginedFutures, out, out)
got, err = e.WebsocketFuturesSubmitOrders(t.Context(), asset.CoinMarginedFutures, out, out)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -99,9 +99,9 @@ func TestWebsocketFuturesCancelOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
got, err := g.WebsocketFuturesCancelOrder(t.Context(), "513160761072", BTCUSDT, asset.USDTMarginedFutures)
got, err := e.WebsocketFuturesCancelOrder(t.Context(), "513160761072", BTCUSDT, asset.USDTMarginedFutures)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -119,9 +119,9 @@ func TestWebsocketFuturesCancelAllOpenFuturesOrders(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
got, err := g.WebsocketFuturesCancelAllOpenFuturesOrders(t.Context(), BTCUSDT, asset.USDTMarginedFutures, "bid")
got, err := e.WebsocketFuturesCancelAllOpenFuturesOrders(t.Context(), BTCUSDT, asset.USDTMarginedFutures, order.Bid.Lower())
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -151,10 +151,10 @@ func TestWebsocketFuturesAmendOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
amend.OrderID = "513170215869"
got, err := g.WebsocketFuturesAmendOrder(t.Context(), amend)
got, err := e.WebsocketFuturesAmendOrder(t.Context(), amend)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -178,10 +178,10 @@ func TestWebsocketFuturesOrderList(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
list.Status = statusOpen
got, err := g.WebsocketFuturesOrderList(t.Context(), list)
got, err := e.WebsocketFuturesOrderList(t.Context(), list)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -199,9 +199,9 @@ func TestWebsocketFuturesGetOrderStatus(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Futures)
e := newExchangeWithWebsocket(t, asset.USDTMarginedFutures) //nolint:govet // Intentional shadow
got, err := g.WebsocketFuturesGetOrderStatus(t.Context(), BTCUSDT, asset.USDTMarginedFutures, "513170215869")
got, err := e.WebsocketFuturesGetOrderStatus(t.Context(), BTCUSDT, asset.USDTMarginedFutures, "513170215869")
require.NoError(t, err)
require.NotEmpty(t, got)
}

View File

@@ -20,15 +20,15 @@ func TestWebsocketLogin(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
c, err := g.Websocket.GetConnection(asset.Spot)
c, err := e.Websocket.GetConnection(asset.Spot)
require.NoError(t, err)
err = g.websocketLogin(t.Context(), c, "")
err = e.websocketLogin(t.Context(), c, "")
require.ErrorIs(t, err, errChannelEmpty)
err = g.websocketLogin(t.Context(), c, "spot.login")
err = e.websocketLogin(t.Context(), c, "spot.login")
require.NoError(t, err)
}
@@ -50,9 +50,9 @@ func TestWebsocketSpotSubmitOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
got, err := g.WebsocketSpotSubmitOrder(t.Context(), out)
got, err := e.WebsocketSpotSubmitOrder(t.Context(), out)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -78,15 +78,15 @@ func TestWebsocketSpotSubmitOrders(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
// test single order
got, err := g.WebsocketSpotSubmitOrders(t.Context(), out)
got, err := e.WebsocketSpotSubmitOrders(t.Context(), out)
require.NoError(t, err)
require.NotEmpty(t, got)
// test batch orders
got, err = g.WebsocketSpotSubmitOrders(t.Context(), out, out)
got, err = e.WebsocketSpotSubmitOrders(t.Context(), out, out)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -100,9 +100,9 @@ func TestWebsocketSpotCancelOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
got, err := g.WebsocketSpotCancelOrder(t.Context(), "644913098758", BTCUSDT, "")
got, err := e.WebsocketSpotCancelOrder(t.Context(), "644913098758", BTCUSDT, "")
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -122,10 +122,10 @@ func TestWebsocketSpotCancelAllOrdersByIDs(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
out.OrderID = "644913101755"
got, err := g.WebsocketSpotCancelAllOrdersByIDs(t.Context(), []WebsocketOrderBatchRequest{out})
got, err := e.WebsocketSpotCancelAllOrdersByIDs(t.Context(), []WebsocketOrderBatchRequest{out})
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -137,9 +137,9 @@ func TestWebsocketSpotCancelAllOrdersByPair(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
got, err := g.WebsocketSpotCancelAllOrdersByPair(t.Context(), currency.EMPTYPAIR, order.Buy, "")
got, err := e.WebsocketSpotCancelAllOrdersByPair(t.Context(), currency.EMPTYPAIR, order.Buy, "")
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -166,10 +166,10 @@ func TestWebsocketSpotAmendOrder(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
amend.OrderID = "645029162673"
got, err := g.WebsocketSpotAmendOrder(t.Context(), amend)
got, err := e.WebsocketSpotAmendOrder(t.Context(), amend)
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -185,9 +185,9 @@ func TestWebsocketSpotGetOrderStatus(t *testing.T) {
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
testexch.UpdatePairsOnce(t, e)
g := newExchangeWithWebsocket(t, asset.Spot)
e := newExchangeWithWebsocket(t, asset.Spot) //nolint:govet // Intentional shadow
got, err := g.WebsocketSpotGetOrderStatus(t.Context(), "644999650452", BTCUSDT, "")
got, err := e.WebsocketSpotGetOrderStatus(t.Context(), "644999650452", BTCUSDT, "")
require.NoError(t, err)
require.NotEmpty(t, got)
}
@@ -206,30 +206,6 @@ func newExchangeWithWebsocket(t *testing.T, a asset.Item) *Exchange {
e.API.AuthenticatedWebsocketSupport = true
e.SetCredentials(apiKey, apiSecret, "", "", "", "")
e.Websocket.SetCanUseAuthenticatedEndpoints(true)
switch a {
case asset.Spot:
avail, err := e.GetAvailablePairs(a)
require.NoError(t, err)
if len(avail) > 1 { // reduce pairs to 1 to speed up tests
avail = avail[:1]
}
require.NoError(t, e.SetPairs(avail, a, true))
case asset.Futures:
avail, err := e.GetAvailablePairs(a)
require.NoError(t, err)
usdtPairs, err := avail.GetPairsByQuote(currency.USDT) // Get USDT margin pairs
require.NoError(t, err)
btcPairs, err := avail.GetPairsByQuote(currency.USD) // Get BTC margin pairs
require.NoError(t, err)
// below makes sure there is both a USDT and BTC pair available
// so that allows two connections to be made.
avail[0] = usdtPairs[0]
avail[1] = btcPairs[0]
avail = avail[:2]
require.NoError(t, e.SetPairs(avail, a, true))
default:
require.NoError(t, e.CurrencyPairs.SetAssetEnabled(a, false))
}
// Disable all other asset types to ensure only the specified asset type is used for websocket tests.
for _, enabled := range e.GetAssetTypes(true) {

View File

@@ -1124,65 +1124,73 @@ func (e *Exchange) CancelBatchOrders(ctx context.Context, o []order.Cancel) (*or
// CancelAllOrders cancels all orders associated with a currency pair
func (e *Exchange) CancelAllOrders(ctx context.Context, o *order.Cancel) (order.CancelAllResponse, error) {
err := o.Validate()
if err != nil {
return order.CancelAllResponse{}, err
var resp order.CancelAllResponse
if err := o.Validate(); err != nil {
return resp, err
}
var cancelAllOrdersResponse order.CancelAllResponse
cancelAllOrdersResponse.Status = map[string]string{}
fmtPair, err := e.FormatExchangeCurrency(o.Pair, o.AssetType)
if err != nil {
return resp, err
}
var side string
switch {
case o.Side.IsLong():
side = order.Bid.Lower()
case o.Side.IsShort():
side = order.Ask.Lower()
case o.Side == order.UnknownSide, o.Side == order.AnySide:
default:
return resp, fmt.Errorf("%w: %q", order.ErrSideIsInvalid, o.Side)
}
switch o.AssetType {
case asset.Spot, asset.Margin, asset.CrossMargin:
if o.Pair.IsEmpty() {
return order.CancelAllResponse{}, currency.ErrCurrencyPairEmpty
}
var cancel []SpotPriceTriggeredOrder
cancel, err = e.CancelMultipleSpotOpenOrders(ctx, o.Pair, o.AssetType)
cancel, err := e.CancelMultipleSpotOpenOrders(ctx, fmtPair, o.AssetType)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
for x := range cancel {
cancelAllOrdersResponse.Status[strconv.FormatInt(cancel[x].AutoOrderID, 10)] = cancel[x].Status
resp.Add(strconv.FormatInt(cancel[x].AutoOrderID, 10), cancel[x].Status)
}
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.DeliveryFutures:
if o.Pair.IsEmpty() {
return cancelAllOrdersResponse, currency.ErrCurrencyPairEmpty
}
settle, err := getSettlementCurrency(o.Pair, o.AssetType)
settle, err := getSettlementCurrency(fmtPair, o.AssetType)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
var cancel []Order
if o.AssetType == asset.DeliveryFutures {
cancel, err = e.CancelMultipleDeliveryOrders(ctx, o.Pair, o.Side.Lower(), settle)
cancel, err = e.CancelMultipleDeliveryOrders(ctx, fmtPair, side, settle)
} else {
cancel, err = e.CancelMultipleFuturesOpenOrders(ctx, o.Pair, o.Side.Lower(), settle)
cancel, err = e.CancelMultipleFuturesOpenOrders(ctx, fmtPair, side, settle)
}
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
for f := range cancel {
cancelAllOrdersResponse.Status[strconv.FormatInt(cancel[f].ID, 10)] = cancel[f].Status
resp.Add(strconv.FormatInt(cancel[f].ID, 10), cancel[f].FinishAs)
}
case asset.Options:
var underlying currency.Pair
if !o.Pair.IsEmpty() {
underlying, err = e.GetUnderlyingFromCurrencyPair(o.Pair)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
}
cancel, err := e.CancelMultipleOptionOpenOrders(ctx, o.Pair, underlying.String(), o.Side.Lower())
cancel, err := e.CancelMultipleOptionOpenOrders(ctx, fmtPair, underlying.String(), side)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
for x := range cancel {
cancelAllOrdersResponse.Status[strconv.FormatInt(cancel[x].OptionOrderID, 10)] = cancel[x].Status
resp.Add(strconv.FormatInt(cancel[x].OptionOrderID, 10), cancel[x].FinishAs)
}
default:
return cancelAllOrdersResponse, fmt.Errorf("%w asset type: %v", asset.ErrNotSupported, o.AssetType)
return resp, fmt.Errorf("%w asset type: %v", asset.ErrNotSupported, o.AssetType)
}
return cancelAllOrdersResponse, nil
return resp, nil
}
// GetOrderInfo returns order information based on order ID
@@ -2573,13 +2581,14 @@ func (e *Exchange) deriveFuturesWebsocketOrderResponses(responses []*WebsocketFu
}
func (e *Exchange) getSpotOrderRequest(s *order.Submit) (*CreateOrderRequest, error) {
var side string
switch {
case s.Side.IsLong():
s.Side = order.Buy
side = order.Buy.Lower()
case s.Side.IsShort():
s.Side = order.Sell
side = order.Sell.Lower()
default:
return nil, order.ErrSideIsInvalid
return nil, fmt.Errorf("%w: %q", order.ErrSideIsInvalid, s.Side)
}
tif, err := toExchangeTIF(s.TimeInForce, s.Price)
@@ -2588,7 +2597,7 @@ func (e *Exchange) getSpotOrderRequest(s *order.Submit) (*CreateOrderRequest, er
}
return &CreateOrderRequest{
Side: s.Side.Lower(),
Side: side,
Type: s.Type.Lower(),
Account: e.assetTypeToString(s.AssetType),
Amount: types.Number(s.GetTradeAmount(e.GetTradingRequirements())),

View File

@@ -0,0 +1,73 @@
package gateio
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
func TestCancelAllOrders(t *testing.T) {
t.Parallel()
_, err := e.CancelAllOrders(t.Context(), nil)
require.ErrorIs(t, err, order.ErrCancelOrderIsNil)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{Pair: currency.EMPTYPAIR, AssetType: 1336})
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{Pair: currency.NewBTCUSDT(), AssetType: 1336})
require.ErrorIs(t, err, asset.ErrNotSupported)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{
Pair: currency.NewBTCUSDT(),
AssetType: asset.Options,
Side: order.ClosePosition,
})
require.ErrorIs(t, err, order.ErrSideIsInvalid)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{
Pair: currency.NewPair(currency.BTC, currency.EMPTYCODE),
AssetType: asset.USDTMarginedFutures,
Side: order.Long,
})
require.ErrorIs(t, err, errInvalidSettlementQuote)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{
Pair: currency.NewPair(currency.BTC, currency.EMPTYCODE),
AssetType: asset.USDTMarginedFutures,
Side: order.Short,
})
require.ErrorIs(t, err, errInvalidSettlementQuote)
_, err = e.CancelAllOrders(t.Context(), &order.Cancel{
Pair: currency.NewPair(currency.BTC, currency.EMPTYCODE),
AssetType: asset.USDTMarginedFutures,
Side: order.AnySide,
})
require.ErrorIs(t, err, errInvalidSettlementQuote)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
for _, a := range e.GetAssetTypes(false) {
t.Run(a.String(), func(t *testing.T) {
t.Parallel()
r := &order.Cancel{
OrderID: "1",
AccountID: "1",
AssetType: a,
Pair: currency.EMPTYPAIR,
}
_, err := e.CancelAllOrders(t.Context(), r)
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
r.Pair = getPair(t, a)
_, err = e.CancelAllOrders(t.Context(), r)
assert.NoError(t, err)
})
}
}

View File

@@ -815,50 +815,48 @@ func (e *Exchange) CancelBatchOrders(ctx context.Context, o []order.Cancel) (*or
// CancelAllOrders cancels all orders associated with a currency pair
func (e *Exchange) CancelAllOrders(ctx context.Context, req *order.Cancel) (order.CancelAllResponse, error) {
var resp order.CancelAllResponse
if err := req.Validate(); err != nil {
return order.CancelAllResponse{}, err
}
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
return resp, err
}
switch req.AssetType {
case asset.Spot:
if e.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := e.wsCancelAllOrders(ctx)
cancel, err := e.wsCancelAllOrders(ctx)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
cancelAllOrdersResponse.Count = resp.Count
return cancelAllOrdersResponse, err
for i := range cancel.Count {
resp.Add(fmt.Sprintf("Unknown:%d", i+1), "cancelled")
}
return resp, err
}
var emptyOrderOptions OrderInfoOptions
openOrders, err := e.GetOpenOrders(ctx, emptyOrderOptions)
openOrders, err := e.GetOpenOrders(ctx, OrderInfoOptions{})
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
for orderID := range openOrders.Open {
var err error
if e.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
err = e.wsCancelOrders(ctx, []string{orderID})
} else {
_, err = e.CancelExistingOrder(ctx, orderID)
}
if err != nil {
cancelAllOrdersResponse.Status[orderID] = err.Error()
resp.Add(orderID, err.Error())
continue
}
resp.Add(orderID, "cancelled")
}
case asset.Futures:
cancelData, err := e.FuturesCancelAllOrders(ctx, req.Pair)
if err != nil {
return cancelAllOrdersResponse, err
return resp, err
}
for x := range cancelData.CancelStatus.CancelledOrders {
cancelAllOrdersResponse.Status[cancelData.CancelStatus.CancelledOrders[x].OrderID] = "cancelled"
resp.Add(cancelData.CancelStatus.CancelledOrders[x].OrderID, "cancelled")
}
}
return cancelAllOrdersResponse, nil
return resp, nil
}
// GetOrderInfo returns information on a current open order

View File

@@ -1748,3 +1748,10 @@ func TestMarshalOrder(t *testing.T) {
exp := []byte(`{"Exchange":"test","Type":4,"Side":"BUY","Pair":"BTC-USDT","AssetType":"spot","TimeInForce":"","ReduceOnly":false,"Leverage":0,"Price":1000,"Amount":1,"QuoteAmount":0,"TriggerPrice":0,"TriggerPriceType":0,"ClientID":"","ClientOrderID":"","AutoBorrow":false,"MarginType":"multi","RetrieveFees":false,"RetrieveFeeDelay":0,"RiskManagementModes":{"Mode":"","TakeProfit":{"Enabled":false,"TriggerPriceType":0,"Price":0,"LimitPrice":0,"OrderType":0},"StopLoss":{"Enabled":false,"TriggerPriceType":0,"Price":0,"LimitPrice":0,"OrderType":0},"StopEntry":{"Enabled":false,"TriggerPriceType":0,"Price":0,"LimitPrice":0,"OrderType":0}},"Hidden":false,"Iceberg":false,"EndTime":"0001-01-01T00:00:00Z","StopDirection":false,"TrackingMode":0,"TrackingValue":0,"RFQDisabled":false}`)
assert.Equal(t, exp, j)
}
func TestAdd(t *testing.T) {
t.Parallel()
var c CancelAllResponse
c.Add("order1", "cancelled")
assert.Equal(t, "cancelled", c.Status["order1"])
}

View File

@@ -281,7 +281,6 @@ type Cancel struct {
// cancel all orders on an exchange
type CancelAllResponse struct {
Status map[string]string
Count int64
}
// CancelBatchResponse returns the status of orders

View File

@@ -1377,3 +1377,11 @@ func StringToTrackingMode(mode string) TrackingMode {
return UnknownTrackingMode
}
}
// Add adds a new orderID and status to the CancelAllResponse
func (c *CancelAllResponse) Add(orderID, status string) {
if c.Status == nil {
c.Status = make(map[string]string)
}
c.Status[orderID] = status
}