diff --git a/binance/client.py b/binance/client.py index d314b358..73a66ca8 100755 --- a/binance/client.py +++ b/binance/client.py @@ -8204,25 +8204,64 @@ def futures_create_algo_order(self, **params): https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order + :param algoType: required - Only support CONDITIONAL + :type algoType: str :param symbol: required :type symbol: str :param side: required - BUY or SELL :type side: str - :param type: required - STOP, TAKE_PROFIT, STOP_MARKET, TAKE_PROFIT_MARKET, TRAILING_STOP_MARKET + :param positionSide: optional - Default BOTH for One-way Mode; LONG or SHORT for Hedge Mode + :type positionSide: str + :param type: required - STOP_MARKET/TAKE_PROFIT_MARKET/STOP/TAKE_PROFIT/TRAILING_STOP_MARKET :type type: str - :param quantity: optional + :param timeInForce: optional - IOC or GTC or FOK or GTX, default GTC + :type timeInForce: str + :param quantity: optional - Cannot be sent with closePosition=true :type quantity: decimal :param price: optional :type price: decimal :param triggerPrice: optional - Used with STOP, STOP_MARKET, TAKE_PROFIT, TAKE_PROFIT_MARKET :type triggerPrice: decimal - :param algoType: required - CONDITIONAL - :type algoType: str + :param workingType: optional - triggerPrice triggered by: MARK_PRICE, CONTRACT_PRICE. Default CONTRACT_PRICE + :type workingType: str + :param priceMatch: optional - only available for LIMIT/STOP/TAKE_PROFIT order + :type priceMatch: str + :param closePosition: optional - true, false; Close-All, used with STOP_MARKET or TAKE_PROFIT_MARKET + :type closePosition: str + :param priceProtect: optional - "TRUE" or "FALSE", default "FALSE" + :type priceProtect: str + :param reduceOnly: optional - "true" or "false", default "false" + :type reduceOnly: str + :param activatePrice: optional - Used with TRAILING_STOP_MARKET orders + :type activatePrice: decimal + :param callbackRate: optional - Used with TRAILING_STOP_MARKET orders, min 0.1, max 10 + :type callbackRate: decimal + :param clientAlgoId: optional - A unique id among open orders + :type clientAlgoId: str + :param newOrderRespType: optional - "ACK", "RESULT", default "ACK" + :type newOrderRespType: str + :param selfTradePreventionMode: optional - EXPIRE_TAKER, EXPIRE_MAKER, EXPIRE_BOTH, default NONE + :type selfTradePreventionMode: str + :param goodTillDate: optional - order cancel time for timeInForce GTD + :type goodTillDate: long :param recvWindow: optional - the number of milliseconds the request is valid for :type recvWindow: int :returns: API response + .. code-block:: python + + result = client.futures_create_algo_order( + algoType='CONDITIONAL', + symbol='BNBUSDT', + side='SELL', + type='TAKE_PROFIT', + quantity='0.01', + price='750.000', + triggerPrice='750.000', + timeInForce='GTC' + ) + """ if "clientAlgoId" not in params: params["clientAlgoId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() diff --git a/examples/futures_algo_order_examples.py b/examples/futures_algo_order_examples.py new file mode 100644 index 00000000..6cb8ca3e --- /dev/null +++ b/examples/futures_algo_order_examples.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python +""" +Examples of how to use the Futures Algo Order API. + +New Algo Order supports various conditional order types including: +- STOP / STOP_MARKET +- TAKE_PROFIT / TAKE_PROFIT_MARKET +- TRAILING_STOP_MARKET + +This example demonstrates the new parameters and features available +for creating advanced algo orders with TP/SL functionality. +""" + +from binance.client import Client + +# Initialize the client +api_key = '' +api_secret = '' +client = Client(api_key, api_secret) + + +def create_basic_stop_market_order(): + """Create a basic STOP_MARKET algo order""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='STOP_MARKET', + quantity=0.001, + triggerPrice=40000, + workingType='CONTRACT_PRICE' + ) + print("Basic Stop Market Order:", order) + return order + + +def create_take_profit_with_price_protect(): + """Create a TAKE_PROFIT order with price protection enabled""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='TAKE_PROFIT', + quantity=0.001, + price=50000, + triggerPrice=50000, + timeInForce='GTC', + priceProtect='TRUE', + workingType='MARK_PRICE' + ) + print("Take Profit with Price Protection:", order) + return order + + +def create_trailing_stop_market(): + """Create a TRAILING_STOP_MARKET order with activate price and callback rate""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='TRAILING_STOP_MARKET', + quantity=0.001, + activatePrice=48000, # Activate when price reaches this level + callbackRate=1.0, # 1% callback rate + ) + print("Trailing Stop Market:", order) + return order + + +def create_stop_with_stp_mode(): + """Create a STOP order with Self-Trade Prevention mode""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='BUY', + positionSide='LONG', # For hedge mode + type='STOP', + quantity=0.001, + price=42000, + triggerPrice=42000, + timeInForce='GTC', + selfTradePreventionMode='EXPIRE_MAKER' + ) + print("Stop Order with STP Mode:", order) + return order + + +def create_take_profit_with_price_match(): + """Create a TAKE_PROFIT order with price match parameter""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='TAKE_PROFIT', + quantity=0.001, + triggerPrice=50000, + timeInForce='GTC', + priceMatch='OPPONENT', # Match opponent's price + ) + print("Take Profit with Price Match:", order) + return order + + +def create_stop_market_close_position(): + """Create a STOP_MARKET order to close all positions""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='STOP_MARKET', + closePosition='true', # Close all current long positions + triggerPrice=39000, + priceProtect='TRUE' + ) + print("Stop Market Close Position:", order) + return order + + +def create_stop_with_reduce_only(): + """Create a STOP order with reduce only mode""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='STOP', + quantity=0.001, + price=41000, + triggerPrice=41000, + timeInForce='GTC', + reduceOnly='true' # Only reduce position, not increase + ) + print("Stop with Reduce Only:", order) + return order + + +def create_with_good_till_date(): + """Create an algo order with GTD (Good Till Date) time in force""" + import time + # Set expiry to 1 hour from now (timestamp in milliseconds) + expiry_time = int((time.time() + 3600) * 1000) + + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='SELL', + type='TAKE_PROFIT', + quantity=0.001, + price=50000, + triggerPrice=50000, + timeInForce='GTD', + goodTillDate=expiry_time + ) + print("Order with Good Till Date:", order) + return order + + +def create_with_result_response(): + """Create an algo order with RESULT response type for detailed information""" + order = client.futures_create_algo_order( + symbol='BTCUSDT', + side='BUY', + type='STOP_MARKET', + quantity=0.001, + triggerPrice=42000, + newOrderRespType='RESULT' # Get detailed response + ) + print("Order with RESULT response:", order) + return order + + +def query_algo_order(symbol, algo_id): + """Query a specific algo order status""" + order = client.futures_get_algo_order( + symbol=symbol, + algoId=algo_id + ) + print("Algo Order Status:", order) + return order + + +def query_open_algo_orders(symbol=None): + """Query all open algo orders""" + if symbol: + orders = client.futures_get_open_algo_orders(symbol=symbol) + else: + orders = client.futures_get_open_algo_orders() + print("Open Algo Orders:", orders) + return orders + + +def cancel_algo_order(symbol, algo_id): + """Cancel a specific algo order""" + result = client.futures_cancel_algo_order( + symbol=symbol, + algoId=algo_id + ) + print("Cancel Result:", result) + return result + + +def cancel_all_algo_orders(symbol): + """Cancel all open algo orders for a symbol""" + result = client.futures_cancel_all_algo_open_orders(symbol=symbol) + print("Cancel All Result:", result) + return result + + +if __name__ == '__main__': + # Example usage + print("=" * 50) + print("Futures Algo Order Examples") + print("=" * 50) + + # Create different types of algo orders + # Uncomment the examples you want to try + + # order = create_basic_stop_market_order() + # order = create_take_profit_with_price_protect() + # order = create_trailing_stop_market() + # order = create_stop_with_stp_mode() + # order = create_take_profit_with_price_match() + # order = create_with_result_response() + + # Query orders + # orders = query_open_algo_orders('BTCUSDT') + + # Cancel orders + # cancel_algo_order('BTCUSDT', algo_id=12345) + # cancel_all_algo_orders('BTCUSDT') diff --git a/tests/test_async_client_futures.py b/tests/test_async_client_futures.py index 1f796064..f6a82dd6 100644 --- a/tests/test_async_client_futures.py +++ b/tests/test_async_client_futures.py @@ -642,3 +642,146 @@ async def test_futures_cancel_all_algo_open_orders_async(futuresClientAsync): result = await futuresClientAsync.futures_cancel_all_algo_open_orders(symbol="LTCUSDT") assert "code" in result or "msg" in result + +async def test_futures_create_algo_order_with_price_protect_async(futuresClientAsync): + """Test creating an algo order with priceProtect parameter async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + priceProtect="TRUE", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["priceProtect"] is True + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +@pytest.mark.skip(reason="TRAILING_STOP_MARKET with activatePrice may not be fully supported in testnet environment") +async def test_futures_create_algo_order_trailing_stop_async(futuresClientAsync): + """Test creating a TRAILING_STOP_MARKET algo order with activatePrice and callbackRate async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + current_price = float(ticker["lastPrice"]) + + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TRAILING_STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + activatePrice=str(current_price * 1.1), + callbackRate="1.0", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +async def test_futures_create_algo_order_with_stp_mode_async(futuresClientAsync): + """Test creating an algo order with selfTradePreventionMode async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TAKE_PROFIT", + algoType="CONDITIONAL", + quantity=1, + price=10000, + triggerPrice=10000, + timeInForce="GTC", + selfTradePreventionMode="EXPIRE_MAKER", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["selfTradePreventionMode"] == "EXPIRE_MAKER" + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +async def test_futures_create_algo_order_with_price_match_async(futuresClientAsync): + """Test creating an algo order with priceMatch parameter async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TAKE_PROFIT", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=10000, + timeInForce="GTC", + priceMatch="OPPONENT", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["priceMatch"] == "OPPONENT" + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +async def test_futures_create_algo_order_with_new_order_resp_type_async(futuresClientAsync): + """Test creating an algo order with newOrderRespType parameter async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + newOrderRespType="RESULT", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert "algoStatus" in order + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +async def test_futures_create_algo_order_with_working_type_async(futuresClientAsync): + """Test creating an algo order with workingType parameter async""" + ticker = await futuresClientAsync.futures_ticker(symbol="LTCUSDT") + positions = await futuresClientAsync.futures_position_information(symbol="LTCUSDT") + order = await futuresClientAsync.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + workingType="MARK_PRICE", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["workingType"] == "MARK_PRICE" + # Clean up + await futuresClientAsync.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + diff --git a/tests/test_client_futures.py b/tests/test_client_futures.py index e2564e27..cc7977af 100644 --- a/tests/test_client_futures.py +++ b/tests/test_client_futures.py @@ -903,3 +903,149 @@ def test_futures_cancel_all_open_orders_with_conditional_param(futuresClient): # Should return success response assert "code" in result or "msg" in result + +def test_futures_create_algo_order_with_price_protect(futuresClient): + """Test creating an algo order with priceProtect parameter""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + priceProtect="TRUE", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["priceProtect"] is True + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +@pytest.mark.skip(reason="TRAILING_STOP_MARKET with activatePrice may not be fully supported in testnet environment") +def test_futures_create_algo_order_trailing_stop(futuresClient): + """Test creating a TRAILING_STOP_MARKET algo order with activatePrice and callbackRate""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + current_price = float(ticker["lastPrice"]) + + # For SELL trailing stop: activatePrice should be above current price + # For BUY trailing stop: activatePrice should be below current price + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TRAILING_STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + activatePrice=str(current_price * 1.1), # 10% above current price + callbackRate="1.0", # 1% + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +def test_futures_create_algo_order_with_stp_mode(futuresClient): + """Test creating an algo order with selfTradePreventionMode""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TAKE_PROFIT", + algoType="CONDITIONAL", + quantity=1, + price=10000, + triggerPrice=10000, + timeInForce="GTC", + selfTradePreventionMode="EXPIRE_MAKER", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["selfTradePreventionMode"] == "EXPIRE_MAKER" + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +def test_futures_create_algo_order_with_price_match(futuresClient): + """Test creating an algo order with priceMatch parameter""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="SELL", + positionSide=positions[0]["positionSide"], + type="TAKE_PROFIT", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=10000, + timeInForce="GTC", + priceMatch="OPPONENT", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["priceMatch"] == "OPPONENT" + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +def test_futures_create_algo_order_with_new_order_resp_type(futuresClient): + """Test creating an algo order with newOrderRespType parameter""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + newOrderRespType="RESULT", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + # With RESULT response type, we should have more detailed information + assert "algoStatus" in order + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) + + +def test_futures_create_algo_order_with_working_type(futuresClient): + """Test creating an algo order with workingType parameter""" + ticker = futuresClient.futures_ticker(symbol="LTCUSDT") + positions = futuresClient.futures_position_information(symbol="LTCUSDT") + order = futuresClient.futures_create_algo_order( + symbol=ticker["symbol"], + side="BUY", + positionSide=positions[0]["positionSide"], + type="STOP_MARKET", + algoType="CONDITIONAL", + quantity=1, + triggerPrice=1000, + workingType="MARK_PRICE", + ) + assert order["symbol"] == ticker["symbol"] + assert "algoId" in order + assert order["workingType"] == "MARK_PRICE" + # Clean up + futuresClient.futures_cancel_algo_order( + symbol=ticker["symbol"], algoId=order["algoId"] + ) +