Skip to content

Commit 41832c0

Browse files
pcriadoperezclaudecarlosmiei
committed
fix conditional swap order (sammchardy#1639)
* add timeout to jobs * feat: Add support for Binance USDS-M Futures conditional/algo orders This update adds support for Binance's new algo order endpoints for conditional orders, which will be mandatory after 2025-12-09 for order types: STOP, STOP_MARKET, TAKE_PROFIT, TAKE_PROFIT_MARKET, and TRAILING_STOP_MARKET. Changes: - Added new order status enums: ACCEPTED, TRIGGERING, TRIGGERED, FINISHED - Added new dedicated algo order methods: * futures_create_algo_order() / async * futures_get_algo_order() / async * futures_get_all_algo_orders() / async * futures_get_open_algo_orders() / async * futures_cancel_algo_order() / async * futures_cancel_all_algo_open_orders() / async - Updated existing futures order methods to auto-detect and route conditional orders: * futures_create_order() now automatically routes conditional order types to algo endpoint * futures_get_order() supports 'conditional' parameter and algoId/clientAlgoId * futures_get_all_orders() supports 'conditional' parameter * futures_get_open_orders() supports 'conditional' parameter * futures_cancel_order() supports 'conditional' parameter and algoId/clientAlgoId * futures_cancel_all_open_orders() supports 'conditional' parameter - Applied same changes to both sync (client.py) and async (async_client.py) clients References: - https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order - https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Algo-Order - https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order * test: Add comprehensive tests for algo/conditional orders Added tests for both sync and async clients covering: - Creating algo orders via dedicated method - Auto-routing conditional orders in futures_create_order - Getting specific algo orders - Getting all algo orders history - Getting open algo orders - Canceling algo orders - Canceling all algo open orders - Using conditional parameter with existing methods Tests validate both the new dedicated algo order methods and the backward-compatible conditional parameter on existing methods. * fix: Use lowercase 'triggerprice' parameter for algo orders The Binance API expects the trigger price parameter to be lowercase 'triggerprice' not camelCase 'triggerPrice'. Updated both client implementations and tests to use the correct parameter name. Changes: - Updated futures_create_order to convert triggerPrice -> triggerprice - Added handling for both camelCase and lowercase input - Updated all tests to use lowercase 'triggerprice' parameter - Fixes APIError: Mandatory parameter 'triggerprice' was not sent This ensures compatibility with Binance's algo order endpoints which expect lowercase parameter names for trigger prices. * fix: Revert to camelCase 'triggerPrice' per official API docs After reviewing the official Binance API documentation at: https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order The parameter is clearly documented as 'triggerPrice' (camelCase), not 'triggerprice' (lowercase). Example from API docs: - Parameter: triggerPrice (DECIMAL, NO) - Response field: "triggerPrice": "750.000" Changes: - Reverted futures_create_order to use triggerPrice (camelCase) - Updated tests to use triggerPrice (camelCase) - Removed incorrect lowercase conversion The API error message may display parameter names in lowercase, but the actual parameter expected by the API is camelCase as documented. * fix tests * update websockets * fix tests * add tests * default algoType * update readme * fix test id * skip test * fix linting --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: carlosmiei <43336371+carlosmiei@users.noreply.github.com>
1 parent 0e8bc4e commit 41832c0

File tree

10 files changed

+836
-29
lines changed

10 files changed

+836
-29
lines changed

README.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,18 @@ pass `testnet=True` when creating the client.
166166
# fetch weekly klines since it listed
167167
klines = client.get_historical_klines("NEOBTC", Client.KLINE_INTERVAL_1WEEK, "1 Jan, 2017")
168168
169+
# create conditional order using the dedicated method
170+
algo_order = client.futures_create_algo_order(symbol="LTCUSDT", side="BUY", type="STOP_MARKET", quantity=0.1, triggerPrice = 120)
171+
172+
# create conditional order using the create_order method (will redirect to the algoOrder as well)
173+
order2 = await client.futures_create_order(symbol="LTCUSDT", side="BUY", type="STOP_MARKET", quantity=0.1, triggerPrice = 120)
174+
175+
# cancel algo/conditional order
176+
cancel2 = await client.futures_cancel_algo_order(orderId=order2["orderId"], symbol="LTCUSDT")
177+
178+
# fetch open algo/conditional orders
179+
open_orders = await client.futures_get_open_algo_orders(symbol="LTCUSDT")
180+
169181
# create order through websockets
170182
order_ws = client.ws_create_order( symbol="LTCUSDT", side="BUY", type="MARKET", quantity=0.1)
171183
@@ -254,6 +266,7 @@ for more information.
254266
# fetch weekly klines since it listed
255267
klines = await client.get_historical_klines("NEOBTC", Client.KLINE_INTERVAL_1WEEK, "1 Jan, 2017")
256268
269+
257270
# create order through websockets
258271
order_ws = await client.ws_create_order( symbol="LTCUSDT", side="BUY", type="MARKET", quantity=0.1)
259272

binance/async_client.py

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,9 +1888,32 @@ async def futures_loan_interest_history(self, **params):
18881888
)
18891889

18901890
async def futures_create_order(self, **params):
1891-
if "newClientOrderId" not in params:
1892-
params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22()
1893-
return await self._request_futures_api("post", "order", True, data=params)
1891+
# Check if this is a conditional order type that needs to use algo endpoint
1892+
order_type = params.get("type", "").upper()
1893+
conditional_types = [
1894+
"STOP",
1895+
"STOP_MARKET",
1896+
"TAKE_PROFIT",
1897+
"TAKE_PROFIT_MARKET",
1898+
"TRAILING_STOP_MARKET",
1899+
]
1900+
1901+
if order_type in conditional_types:
1902+
# Route to algo order endpoint
1903+
if "clientAlgoId" not in params:
1904+
params["clientAlgoId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22()
1905+
# Remove newClientOrderId if it was added by default
1906+
params.pop("newClientOrderId", None)
1907+
params["algoType"] = "CONDITIONAL"
1908+
# Convert stopPrice to triggerPrice for algo orders (camelCase per API docs)
1909+
if "stopPrice" in params and "triggerPrice" not in params:
1910+
params["triggerPrice"] = params.pop("stopPrice")
1911+
return await self._request_futures_api("post", "algoOrder", True, data=params)
1912+
else:
1913+
# Use regular order endpoint
1914+
if "newClientOrderId" not in params:
1915+
params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22()
1916+
return await self._request_futures_api("post", "order", True, data=params)
18941917

18951918
async def futures_limit_order(self, **params):
18961919
"""Send in a new futures limit order.
@@ -1971,6 +1994,8 @@ async def futures_modify_order(self, **params):
19711994
"""
19721995
return await self._request_futures_api("put", "order", True, data=params)
19731996

1997+
futures_modify_order.__doc__ = Client.futures_modify_order.__doc__
1998+
19741999
async def futures_create_test_order(self, **params):
19752000
return await self._request_futures_api("post", "order/test", True, data=params)
19762001

@@ -1987,21 +2012,66 @@ async def futures_place_batch_order(self, **params):
19872012
)
19882013

19892014
async def futures_get_order(self, **params):
1990-
return await self._request_futures_api("get", "order", True, data=params)
2015+
# Check if this is a request for a conditional/algo order
2016+
is_conditional = params.pop("conditional", False)
2017+
# Also check if algoId or clientAlgoId is provided
2018+
if "algoId" in params or "clientAlgoId" in params:
2019+
is_conditional = True
2020+
2021+
if is_conditional:
2022+
return await self._request_futures_api("get", "algoOrder", True, data=params)
2023+
else:
2024+
return await self._request_futures_api("get", "order", True, data=params)
2025+
2026+
futures_get_order.__doc__ = Client.futures_get_order.__doc__
19912027

19922028
async def futures_get_open_orders(self, **params):
1993-
return await self._request_futures_api("get", "openOrders", True, data=params)
2029+
is_conditional = params.pop("conditional", False)
2030+
2031+
if is_conditional:
2032+
return await self._request_futures_api("get", "openAlgoOrders", True, data=params)
2033+
else:
2034+
return await self._request_futures_api("get", "openOrders", True, data=params)
2035+
2036+
futures_get_open_orders.__doc__ = Client.futures_get_open_orders.__doc__
19942037

19952038
async def futures_get_all_orders(self, **params):
1996-
return await self._request_futures_api("get", "allOrders", True, data=params)
2039+
is_conditional = params.pop("conditional", False)
2040+
2041+
if is_conditional:
2042+
return await self._request_futures_api("get", "allAlgoOrders", True, data=params)
2043+
else:
2044+
return await self._request_futures_api("get", "allOrders", True, data=params)
2045+
2046+
futures_get_all_orders.__doc__ = Client.futures_get_all_orders.__doc__
19972047

19982048
async def futures_cancel_order(self, **params):
1999-
return await self._request_futures_api("delete", "order", True, data=params)
2049+
# Check if this is a request for a conditional/algo order
2050+
is_conditional = params.pop("conditional", False)
2051+
# Also check if algoId or clientAlgoId is provided
2052+
if "algoId" in params or "clientAlgoId" in params:
2053+
is_conditional = True
2054+
2055+
if is_conditional:
2056+
return await self._request_futures_api("delete", "algoOrder", True, data=params)
2057+
else:
2058+
return await self._request_futures_api("delete", "order", True, data=params)
2059+
2060+
futures_cancel_order.__doc__ = Client.futures_cancel_order.__doc__
20002061

20012062
async def futures_cancel_all_open_orders(self, **params):
2002-
return await self._request_futures_api(
2003-
"delete", "allOpenOrders", True, data=params
2004-
)
2063+
is_conditional = params.pop("conditional", False)
2064+
2065+
if is_conditional:
2066+
return await self._request_futures_api(
2067+
"delete", "algoOpenOrders", True, data=params
2068+
)
2069+
else:
2070+
return await self._request_futures_api(
2071+
"delete", "allOpenOrders", True, data=params
2072+
)
2073+
2074+
futures_cancel_all_open_orders.__doc__ = Client.futures_cancel_all_open_orders.__doc__
20052075

20062076
async def futures_cancel_orders(self, **params):
20072077
if params.get("orderidlist"):
@@ -2021,6 +2091,44 @@ async def futures_countdown_cancel_all(self, **params):
20212091
"post", "countdownCancelAll", True, data=params
20222092
)
20232093

2094+
# Algo Orders (Conditional Orders)
2095+
2096+
async def futures_create_algo_order(self, **params):
2097+
if "clientAlgoId" not in params:
2098+
params["clientAlgoId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22()
2099+
if "algoType" not in params:
2100+
params["algoType"] = "CONDITIONAL"
2101+
return await self._request_futures_api("post", "algoOrder", True, data=params)
2102+
2103+
futures_create_algo_order.__doc__ = Client.futures_create_algo_order.__doc__
2104+
2105+
async def futures_cancel_algo_order(self, **params):
2106+
return await self._request_futures_api("delete", "algoOrder", True, data=params)
2107+
2108+
futures_cancel_algo_order.__doc__ = Client.futures_cancel_algo_order.__doc__
2109+
2110+
async def futures_cancel_all_algo_open_orders(self, **params):
2111+
return await self._request_futures_api(
2112+
"delete", "algoOpenOrders", True, data=params
2113+
)
2114+
2115+
futures_cancel_all_algo_open_orders.__doc__ = Client.futures_cancel_all_algo_open_orders.__doc__
2116+
2117+
async def futures_get_algo_order(self, **params):
2118+
return await self._request_futures_api("get", "algoOrder", True, data=params)
2119+
2120+
futures_get_algo_order.__doc__ = Client.futures_get_algo_order.__doc__
2121+
2122+
async def futures_get_open_algo_orders(self, **params):
2123+
return await self._request_futures_api("get", "openAlgoOrders", True, data=params)
2124+
2125+
futures_get_open_algo_orders.__doc__ = Client.futures_get_open_algo_orders.__doc__
2126+
2127+
async def futures_get_all_algo_orders(self, **params):
2128+
return await self._request_futures_api("get", "allAlgoOrders", True, data=params)
2129+
2130+
futures_get_all_algo_orders.__doc__ = Client.futures_get_all_algo_orders.__doc__
2131+
20242132
async def futures_account_balance(self, **params):
20252133
return await self._request_futures_api(
20262134
"get", "balance", True, version=3, data=params
@@ -3973,6 +4081,17 @@ async def ws_futures_account_status(self, **params):
39734081
"""
39744082
return await self._ws_futures_api_request("account.status", True, params)
39754083

4084+
async def ws_futures_create_algo_order(self, **params):
4085+
if "clientAlgoId" not in params:
4086+
params["clientAlgoId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22()
4087+
return await self._ws_futures_api_request("algoOrder.place", True, params)
4088+
ws_futures_create_algo_order.__doc__ = Client.ws_futures_create_algo_order.__doc__
4089+
4090+
async def ws_futures_cancel_algo_order(self, **params):
4091+
return await self._ws_futures_api_request("algoOrder.cancel", True, params)
4092+
4093+
ws_futures_cancel_algo_order.__doc__ = Client.ws_futures_cancel_algo_order.__doc__
4094+
39764095
####################################################
39774096
# Gift Card API Endpoints
39784097
####################################################

0 commit comments

Comments
 (0)