diff --git a/binance/client.py b/binance/client.py index df39aeab3..2e87df45e 100755 --- a/binance/client.py +++ b/binance/client.py @@ -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. @@ -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 @@ -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. @@ -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 @@ -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) @@ -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__ @@ -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 diff --git a/binance/enums.py b/binance/enums.py index 164e94a0c..a1f6ae54d 100644 --- a/binance/enums.py +++ b/binance/enums.py @@ -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" diff --git a/tests/test_ids.py b/tests/test_ids.py index 27ade3cc6..814c0c7e3 100644 --- a/tests/test_ids.py +++ b/tests/test_ids.py @@ -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) @@ -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")