# Centralized handler for Binance's terrible API design (effective Dec 9, 2025)

# Conditional orders (STOP_MARKET) moved to separate /algoOrder endpoints

# This class abstracts the mess so rest of code stays clean



class BinanceOrderManager:

"""Handles routing between regular and algo order APIs + normalization"""

CONDITIONAL_TYPES = {'STOP_MARKET'}

def init(self, client):

self.client = client

def isconditional(self, order_type):

"""Check if order type requires algo API"""

return order_type in self.CONDITIONAL_TYPES

def create_order(self, symbol, side, order_type, **params):

"""

Unified order creation - routes to correct API

Args:

symbol: Trading pair (e.g., 'BTCUSDT')

side: 'BUY' or 'SELL'

order_type: 'MARKET', 'LIMIT', 'STOP_MARKET', etc.

**params: Order parameters (quantity, price, stopPrice, etc.)

Returns:

Order response with normalized fields

"""

if self._is_conditional(order_type):

# Algo API: Map stopPrice → triggerPrice, add algoType

algo_params = {

'symbol': symbol,

'side': side,

'algoType': 'CONDITIONAL',

'quantity': params.get('quantity'),

'triggerPrice': params.get('stopPrice'), # KEY MAPPING

}

# Algo API doesn't support reduceOnly parameter - omit it

if 'timeInForce' in params:

algo_params['timeInForce'] = params['timeInForce']

if 'closePosition' in params:

algo_params['closePosition'] = params['closePosition']

response = self.client.futures_create_algo_order(**algo_params)

# Normalize response: algoId → orderId, triggerPrice → stopPrice

return {

'orderId': response.get('algoId'),

'algoId': response.get('algoId'),

'symbol': response.get('symbol'),

'type': order_type,

'side': response.get('side'),

'stopPrice': response.get('triggerPrice'),

'origQty': response.get('totalQty'),

'status': response.get('algoStatus'),

'isAlgoOrder': True

}

else:

# Regular API: Use as-is

response = self.client.futures_create_order(

symbol=symbol,

side=side,

type=order_type,

**params

)

response['isAlgoOrder'] = False

return response

def get_open_orders(self, symbol):

"""

Fetch ALL open orders (regular + algo) and normalize

Returns:

List of orders with unified structure (all have orderId, stopPrice, etc.)

"""

try:

# Fetch from both APIs

regular_orders = self.client.futures_get_open_orders(symbol=symbol)

algo_orders = self.client.futures_get_open_algo_orders(symbol=symbol)

# Normalize algo orders to match regular structure

normalized_algo = []

for order in algo_orders:

normalized_algo.append({

'orderId': order.get('algoId'),

'algoId': order.get('algoId'),

'symbol': order.get('symbol'),

'type': order.get('algoType', 'STOP_MARKET'),

'side': order.get('side'),

'price': order.get('price'),

'stopPrice': order.get('triggerPrice'), # KEY MAPPING

'origQty': order.get('totalQty'),

'executedQty': order.get('executedQty', 0),

'status': order.get('algoStatus'),

'timeInForce': order.get('timeInForce'),

'reduceOnly': order.get('reduceOnly', False),

'closePosition': order.get('closePosition', False),

'updateTime': order.get('updateTime'),

'isAlgoOrder': True

})

return regular_orders + normalized_algo

except:

return []

def cancel_order(self, symbol, order_id, is_algo=None):

"""

Cancel single order - auto-detects if algo order

Args:

symbol: Trading pair

order_id: Order ID (or algoId)

is_algo: If known, pass True/False. Otherwise auto-detect via API calls.

Returns:

Cancellation response

"""

if is_algo is None:

# Try regular first, fall back to algo

try:

return self.client.futures_cancel_order(symbol=symbol, orderId=order_id)

except:

return self.client.futures_cancel_algo_order(symbol=symbol, algoId=order_id)

elif is_algo:

return self.client.futures_cancel_algo_order(symbol=symbol, algoId=order_id)

else:

return self.client.futures_cancel_order(symbol=symbol, orderId=order_id)

def cancel_all_orders(self, symbol):

"""Cancel ALL orders (regular + algo) for symbol"""

try:

self.client.futures_cancel_all_open_orders(symbol=symbol)

self.client.futures_cancel_all_algo_open_orders(symbol=symbol)

return True

except Exception as e:

print(f"❌ Error cancelling orders: {e}")

return False