Binance Spot API not removing a trade after cancel

Hi,

I am testing on BTC/FDUSD pair, where market makers have 0 fees.
I am testing with LIMIT_MAKER orders, small amounts (about $5 each time, posted at latest bid/ask price).

I noticed that sometimes, when I submit a LIMIT_MAKER order, and it gets filled nearly immediately, it gets stuck and never gets removed, despite repeated attempts to cancel on my part through API. I only know that’s it’s filled because of the changes in amounts of currencies on the account, otherwise it behaves as if it never got matched.

Specifics (removed http://api.binance.com/ from the requests, because “new users can only post one link”):

  1. Opening a trade

Request:
POST api/v3/order?symbol=BTCFDUSD&side=SELL&type=LIMIT_MAKER&price=104954.810000&newOrderRespType=RESULT&quantity=0.000050

Reply:
{“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“transactTime”:1737927542355,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“origQuoteOrderQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“workingTime”:1737927542355,“selfTradePreventionMode”:“EXPIRE_MAKER”}

At this point, if I look at the account balances, I see that the transaction went through, because they changed.

Now,

  1. Getting trade info:

Request:
GET api/v3/order?symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx

Reply:
{“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

As you see, it’s as if it was never filled. And now,

  1. Trying to cancel it

Request:
DELETE api/v3/order?symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx

Reply:
{“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

I did the last one a hundred times in about a minute, and the reply is the same every time - returning order as NEW, not CANCELED.

I can see the orders present also in the Binance App - and if I cancel them from there, they do get canceled. Tried it just now.

I could work around this, by doing trades one by one, checking balance changes on the account and figuring out from there that the trade actually got filled, but this leaves the trade remnant handing in the open trades list. And there are limits on it.

So I think that that’s not a good look if I have to periodically manually cancel orders that got executed and then left in the order book somehow…

Could you please confirm that you use DELETE method when canceling the order via the API?

GET and DELETE endpoints have exact same parameter lists, you might be accidentally calling the GET one.

Cancel is never expected to return you NEW status. Either you get the order canceled, or you get an error explaining why it could not be canceled.

Yes, I definitely used DELETE. And 99% of the time it works correctly - if I had used GET, it wouldn’t work at all, but it cancels fine 99% of the time. It only failed for me in the circumstances I described, but it failed repeatedly. I don’t know why, but I imagine that maybe timing of calls and maybe the distance of first and second order from the current price matter.

Here’s the specific log of such a thing happening, but this time I include all the trading calls to the API I made in the entire run (so excluding connecting websocket for data feed, exchangeInfo, historic klines, time and account API calls, as they’re just reads abnd also I don’t have precise logs for them):

// Server = https://api.binance.com/api/

Order (POST): symbol=BTCFDUSD&side=BUY&type=LIMIT_MAKER&price=104858.190000&newOrderRespType=RESULT&quantity=0.000050
Order reply: {“symbol”:“BTCFDUSD”,“orderId”:12685002024,“orderListId”:-1,“clientOrderId”:“orE2lcrVrMaq3NVejd0CAw”,“transactTime”:1737927480652,“price”:“104858.19000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“origQuoteOrderQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“BUY”,“workingTime”:1737927480652,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Order info request (GET): symbol=BTCFDUSD&origClientOrderId=orE2lcrVrMaq3NVejd0CAw
Order info reply: {“symbol”:“BTCFDUSD”,“orderId”:12685002024,“orderListId”:-1,“clientOrderId”:“orE2lcrVrMaq3NVejd0CAw”,“price”:“104858.19000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“BUY”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927480652,“updateTime”:1737927480652,“isWorking”:true,“workingTime”:1737927480652,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Order info request (GET): symbol=BTCFDUSD&origClientOrderId=orE2lcrVrMaq3NVejd0CAw
Order info reply: {“symbol”:“BTCFDUSD”,“orderId”:12685002024,“orderListId”:-1,“clientOrderId”:“orE2lcrVrMaq3NVejd0CAw”,“price”:“104858.19000000”,“origQty”:“0.00005000”,“executedQty”:“0.00005000”,“cummulativeQuoteQty”:“5.24290950”,“status”:“FILLED”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“BUY”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927480652,“updateTime”:1737927481503,“isWorking”:true,“workingTime”:1737927480652,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Order (POST): symbol=BTCFDUSD&side=SELL&type=LIMIT_MAKER&price=104954.810000&newOrderRespType=RESULT&quantity=0.000050
Order reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“transactTime”:1737927542355,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“origQuoteOrderQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“workingTime”:1737927542355,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Order info request (GET): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Order info reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}
Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

Cancel request (DELETE): symbol=BTCFDUSD&origClientOrderId=eppJ77KTnbvPyIFvWnW8Jx
Cancel reply: {“symbol”:“BTCFDUSD”,“orderId”:12685007967,“orderListId”:-1,“clientOrderId”:“eppJ77KTnbvPyIFvWnW8Jx”,“price”:“104954.81000000”,“origQty”:“0.00005000”,“executedQty”:“0.00000000”,“cummulativeQuoteQty”:“0.00000000”,“status”:“NEW”,“timeInForce”:“GTC”,“type”:“LIMIT_MAKER”,“side”:“SELL”,“stopPrice”:“0.00000000”,“icebergQty”:“0.00000000”,“time”:1737927542355,“updateTime”:1737927542355,“isWorking”:true,“workingTime”:1737927542355,“origQuoteOrderQty”:“0.00000000”,“selfTradePreventionMode”:“EXPIRE_MAKER”}

(and many more cancel requests)

@Krystian_Galaj just to confirm, the “Cancel reply” in your logs is the API response to DELETE request?

You are not doing something like

  1. Send DELETE to cancel an order
  2. Call GET to check the order status after cancel

right?

The sequence above can result in stale responses to GET, as there is some propagation delay. DELETE response gives you the correct status immediately, which is reflected in GET responses a bit later.

For example, here’s how it looks for me in Testnet in I do a quick sequence of POST, GET, DELETE, GET:

<<< POST https://testnet.binance.vision/api/v3/order {'symbol': 'BTCFDUSD', 'type': 'LIMIT_MAKER', 'side': 'BUY', 'quantity': '0.01', 'price': '50000.00', 'newClientOrderId': 'cancel_test', 'newOrderRespType': 'RESULT', 'timestamp': 1738117921991, 'signature': '09f069c01c9194cb015ae0235aad511d1119d1f09242a69df7eb39f52ba9e6ca'}
>>> {"symbol":"BTCFDUSD","orderId":4094017,"orderListId":-1,"clientOrderId":"cancel_test","transactTime":1738117922273,"price":"50000.00000000","origQty":"0.01000000","executedQty":"0.00000000","origQuoteOrderQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","workingTime":1738117922273,"selfTradePreventionMode":"EXPIRE_MAKER"}
<<< GET https://testnet.binance.vision/api/v3/order {'symbol': 'BTCFDUSD', 'origClientOrderId': 'cancel_test', 'timestamp': 1738117922167, 'signature': 'b66bf5cf7b2a76021b279f3a400b713775172b101d64bd24a3d0c0f2b9a9bf00'}
>>> {"symbol":"BTCFDUSD","orderId":4094017,"orderListId":-1,"clientOrderId":"cancel_test","price":"50000.00000000","origQty":"0.01000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1738117922273,"updateTime":1738117922273,"isWorking":true,"workingTime":1738117922273,"origQuoteOrderQty":"0.00000000","selfTradePreventionMode":"EXPIRE_MAKER"}
<<< DELETE https://testnet.binance.vision/api/v3/order {'symbol': 'BTCFDUSD', 'origClientOrderId': 'cancel_test', 'timestamp': 1738117922287, 'signature': '93a8e2810ae505f70515dc97ca2ee70b9e9d6d38f87e166804ffaa5c8405dc79'}
>>> {"symbol":"BTCFDUSD","origClientOrderId":"cancel_test","orderId":4094017,"orderListId":-1,"clientOrderId":"X6DhwNLyJ1qIvCOv3TD6wP","transactTime":1738117922506,"price":"50000.00000000","origQty":"0.01000000","executedQty":"0.00000000","origQuoteOrderQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","selfTradePreventionMode":"EXPIRE_MAKER"}
<<< GET https://testnet.binance.vision/api/v3/order {'symbol': 'BTCFDUSD', 'origClientOrderId': 'cancel_test', 'timestamp': 1738117922397, 'signature': '4b4e45ee1f91c3e6b7d2df3291beae70575493a17ac07c59bb78baa2c525eac5'}
>>> {"symbol":"BTCFDUSD","orderId":4094017,"orderListId":-1,"clientOrderId":"cancel_test","price":"50000.00000000","origQty":"0.01000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"NEW","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1738117922273,"updateTime":1738117922273,"isWorking":true,"workingTime":1738117922273,"origQuoteOrderQty":"0.00000000","selfTradePreventionMode":"EXPIRE_MAKER"}
<<< GET https://testnet.binance.vision/api/v3/order {'symbol': 'BTCFDUSD', 'origClientOrderId': 'cancel_test', 'timestamp': 1738117923051, 'signature': 'af0ae5c4b1953bb9f7fd0536719b106736ef2a1eef14a8dfd9bc09f3fa7689a3'}
>>> {"symbol":"BTCFDUSD","orderId":4094017,"orderListId":-1,"clientOrderId":"cancel_test","price":"50000.00000000","origQty":"0.01000000","executedQty":"0.00000000","cummulativeQuoteQty":"0.00000000","status":"CANCELED","timeInForce":"GTC","type":"LIMIT_MAKER","side":"BUY","stopPrice":"0.00000000","icebergQty":"0.00000000","time":1738117922273,"updateTime":1738117922506,"isWorking":true,"workingTime":1738117922273,"origQuoteOrderQty":"0.00000000","selfTradePreventionMode":"EXPIRE_MAKER"}

Note how GET keeps returning "status":"NEW" for some time, even after DELETE immediately responded with "status":"CANCELED", but after a short delay GET returns the correct status.

Ah… I also tried cancelling all open orders for the symbol with a separate call, and it gave me the same answer, but within a single-element JSON array. But I don’t have it in this log.

That’s right, the Cancel reply is the API response to DELETE request. I am not sending any GETs after a DELETE, I assume that a reply from DELETE request will tell me that the order is CANCELED, and that the order state variables sent in this reply represent the final state of the order, for example if it was partially filled.

Yes, that’s right. DELETE responds with the final status of the order, which is CANCELED even if it was canceled after a partial fill.

So, the problem seems to be that DELETE sometimes responds as if it was a GET, and once it starts responding in this way to some order, the only way to cancel it is to do it from the Binance app manually.