Skip to content

Commit

Permalink
added cancel_replace_order, cancel_all_open_orders and cancel_all_ope…
Browse files Browse the repository at this point in the history
…n_margin_orders to client.py (#1475)

* added cancel_replace_order, cancel_all_open_orders and cancel_all_open_margin_orders to client.py

* added TIME_IN_FORCE_GTD

* added parameters for get_current_order_count()

* added parameters for get_current_order_count()

* fix params

* changed quotation marks

* add missing version

* add id

* space

* add tests

---------

Co-authored-by: m-HD <none>
Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
  • Loading branch information
m-HD and carlosmiei authored Nov 17, 2024
1 parent 1867c30 commit 6866ca5
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 5 deletions.
137 changes: 132 additions & 5 deletions binance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2160,6 +2160,117 @@ def cancel_order(self, **params):
"""
return self._delete("order", True, data=params)

def cancel_all_open_orders(self, **params):
return self._delete("openOrders", True, data=params)

def cancel_replace_order(self, **params):
"""Cancels an existing order and places a new order on the same symbol.
Filters and Order Count are evaluated before the processing of the cancellation and order placement occurs.
A new order that was not attempted (i.e. when newOrderResult: NOT_ATTEMPTED), will still increase the order count by 1.
https://binance-docs.github.io/apidocs/spot/en/#cancel-an-existing-order-and-send-a-new-order-trade
:param symbol: required
:type symbol: str
:param side: required
:type side: enum
:param type: required
:type type: enum
:param cancelReplaceMode: required - STOP_ON_FAILURE or ALLOW_FAILURE
:type cancelReplaceMode: enum
:param timeInForce: optional
:type timeInForce: enum
:param quantity: optional
:type quantity: decimal
:param quoteOrderQty: optional
:type quoteOrderQty: decimal
:param price: optional
:type price: decimal
:param cancelNewClientOrderId: optional - Used to uniquely identify this cancel. Automatically generated by default.
:type cancelNewClientOrderId: str
:param cancelOrigClientOrderId: optional - Either the cancelOrigClientOrderId or cancelOrderId must be provided. If both are provided, cancelOrderId takes precedence.
:type cancelOrigClientOrderId: str
:param cancelOrderId: optional - Either the cancelOrigClientOrderId or cancelOrderId must be provided. If both are provided, cancelOrderId takes precedence.
:type cancelOrderId: long
:param newClientOrderId: optional - Used to identify the new order.
:type newClientOrderId: str
:param strategyId: optional
:type strategyId: int
:param strategyType: optional - The value cannot be less than 1000000.
:type strategyType: int
:param stopPrice: optional
:type stopPrice: decimal
:param trailingDelta: optional
:type trailingDelta: long
:param icebergQty: optional
:type icebergQty: decimal
:param newOrderRespType: optional - ACK, RESULT or FULL. MARKET and LIMIT orders types default to FULL; all other orders default to ACK
:type newOrderRespType: enum
:param selfTradePreventionMode: optional - EXPIRE_TAKER, EXPIRE_MAKER, EXPIRE_BOTH or NONE.
:type selfTradePreventionMode: enum
:param cancelRestrictions: optional - ONLY_NEW or ONLY_PARTIALLY_FILLED
:type cancelRestrictions: enum
:param recvWindow: optional - The value cannot be greater than 60000
:type recvWindow: int
:returns: API response
.. code-block:: python
//Both the cancel order placement and new order placement succeeded.
{
"cancelResult": "SUCCESS",
"newOrderResult": "SUCCESS",
"cancelResponse": {
"symbol": "BTCUSDT",
"origClientOrderId": "DnLo3vTAQcjha43lAZhZ0y",
"orderId": 9,
"orderListId": -1,
"clientOrderId": "osxN3JXAtJvKvCqGeMWMVR",
"transactTime": 1684804350068,
"price": "0.01000000",
"origQty": "0.000100",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "CANCELED",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "SELL",
"selfTradePreventionMode": "NONE"
},
"newOrderResponse": {
"symbol": "BTCUSDT",
"orderId": 10,
"orderListId": -1,
"clientOrderId": "wOceeeOzNORyLiQfw7jd8S",
"transactTime": 1652928801803,
"price": "0.02000000",
"origQty": "0.040000",
"executedQty": "0.00000000",
"cummulativeQuoteQty": "0.00000000",
"status": "NEW",
"timeInForce": "GTC",
"type": "LIMIT",
"side": "BUY",
"workingTime": 1669277163808,
"fills": [],
"selfTradePreventionMode": "NONE"
}
}
Similar to POST /api/v3/order, additional mandatory parameters are determined by type.
Response format varies depending on whether the processing of the message succeeded, partially succeeded, or failed.
:raises: BinanceRequestException, BinanceAPIException
"""
if "newClientOrderId" not in params:
params["newClientOrderId"] = self.SPOT_ORDER_PREFIX + self.uuid22()
return self._post("order/cancelReplace", True, data=params)

def get_open_orders(self, **params):
"""Get all open orders on a symbol.
Expand Down Expand Up @@ -2341,10 +2452,10 @@ def get_my_trades(self, **params):
"""
return self._get("myTrades", True, data=params)

def get_current_order_count(self):
def get_current_order_count(self, **params):
"""Displays the user's current order count usage for all intervals.
https://binance-docs.github.io/apidocs/spot/en/#query-current-order-count-usage-trade
https://binance-docs.github.io/apidocs/spot/en/#query-unfilled-order-count-user_data
:returns: API response
Expand All @@ -2368,7 +2479,7 @@ def get_current_order_count(self):
]
"""
return self._get("rateLimit/order", True)
return self._get("rateLimit/order", True, data=params)

def get_prevented_matches(self, **params):
"""Displays the list of orders that were expired because of STP.
Expand Down Expand Up @@ -4717,6 +4828,9 @@ def cancel_margin_order(self, **params):
"delete", "margin/order", signed=True, data=params
)

def cancel_all_open_margin_orders(self, **params):
return self._request_margin_api("delete", "margin/openOrders", signed=True, data=params)

def set_margin_max_leverage(self, **params):
"""Adjust cross margin max leverage
Expand Down Expand Up @@ -11065,6 +11179,14 @@ async def cancel_order(self, **params):

cancel_order.__doc__ = Client.cancel_order.__doc__

async def cancel_all_open_orders(self, **params):
return await self._delete("openOrders", True, data=params)

async def cancel_replace_order(self, **params):
if "newClientOrderId" not in params:
params["newClientOrderId"] = self.SPOT_ORDER_PREFIX + self.uuid22()
return await self._post("order/cancelReplace", signed=True, data=params)

async def get_open_orders(self, **params):
return await self._get("openOrders", True, data=params)

Expand Down Expand Up @@ -11097,8 +11219,8 @@ async def get_my_trades(self, **params):

get_my_trades.__doc__ = Client.get_my_trades.__doc__

async def get_current_order_count(self):
return await self._get("rateLimit/order", True)
async def get_current_order_count(self, **params):
return await self._get("rateLimit/order", True, data=params)

get_current_order_count.__doc__ = Client.get_current_order_count.__doc__

Expand Down Expand Up @@ -11543,6 +11665,11 @@ async def cancel_margin_order(self, **params):

cancel_margin_order.__doc__ = Client.cancel_margin_order.__doc__

async def cancel_all_open_margin_orders(self, **params):
return await self._request_margin_api("delete", "margin/openOrders", signed=True, data=params)

cancel_all_open_margin_orders.__doc__ = Client.cancel_all_open_margin_orders.__doc__

async def set_margin_max_leverage(self, **params):
return await self._request_margin_api(
"post", "margin/max-leverage", signed=True, data=params
Expand Down
1 change: 1 addition & 0 deletions binance/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
TIME_IN_FORCE_IOC = "IOC" # Immediate or cancel
TIME_IN_FORCE_FOK = "FOK" # Fill or kill
TIME_IN_FORCE_GTX = "GTX" # Post only order
TIME_IN_FORCE_GTD = "GTD" # Good till date

ORDER_RESP_TYPE_ACK = "ACK"
ORDER_RESP_TYPE_RESULT = "RESULT"
Expand Down
31 changes: 31 additions & 0 deletions tests/test_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ def test_spot_market_id():
assert url_dict["newClientOrderId"].startswith("x-HNA2TXFJ")


def test_spot_cancel_replace_id():
with requests_mock.mock() as m:
m.post("https://api.binance.com/api/v3/order/cancelReplace", json={}, status_code=200)
client.cancel_replace_order(
cancelOrderId="orderId", symbol="LTCUSDT", side="BUY", type="MARKET", quantity=0.1
)
url_dict = dict(pair.split("=") for pair in m.last_request.text.split("&"))
assert url_dict["newClientOrderId"].startswith("x-HNA2TXFJ")

def test_swap_id():
with requests_mock.mock() as m:
m.post("https://fapi.binance.com/fapi/v1/order", json={}, status_code=200)
Expand Down Expand Up @@ -147,6 +156,28 @@ def handler(url, **kwargs):
await clientAsync.close_connection()


@pytest.mark.asyncio()
async def test_spot_cancel_replace_id_async():
clientAsync = AsyncClient(
api_key="api_key", api_secret="api_secret"
) # reuse client later
with aioresponses() as m:

def handler(url, **kwargs):
client_order_id = kwargs["data"][0][1]
assert client_order_id.startswith("x-HNA2TXFJ")

m.post(
"https://api.binance.com/api/v3/order/cancelReplace",
payload={"id": 1},
status=200,
callback=handler,
)
await clientAsync.cancel_replace_order(
orderId="id", symbol="LTCUSDT", side="BUY", type="MARKET", quantity=0.1
)
await clientAsync.close_connection()

@pytest.mark.asyncio()
async def test_swap_id_async():
clientAsync = AsyncClient(api_key="api_key", api_secret="api_secret")
Expand Down

0 comments on commit 6866ca5

Please sign in to comment.