binance_place_oco_order
Place a Binance OCO bracket order: submit a take-profit and stop-loss pair atomically so one fill cancels the other.
Instructions
Place a REAL one-cancels-the-other pair (take-profit + stop). This spends real money.
Calls POST /api/v3/orderList/oco (SIGNED, IP weight 1, unfilled-order count 2). Both
legs carry the same quantity and the same side; when one triggers, Binance cancels
the other. This is the bracket around a position you already hold (SELL) or the
breakout/dip pair for one you want (BUY).
Kill-switch. This call is refused with Error: … trading is disabled … unless the
server runs with BINANCE_ALLOW_TRADING=1. The gate lives in the HTTP client, so no
tool can bypass it. If you see that error, the operator has deliberately put the server
in read-only mode — report it, do not try to work around it.
There is no dry-run for a list. binance_test_order validates ONE order, not a
list; run it per leg if you want Binance's filter check before committing.
Leg rules, enforced locally before anything is signed:
exactly one take-profit leg (LIMIT_MAKER / TAKE_PROFIT / TAKE_PROFIT_LIMIT) and one stop leg (STOP_LOSS / STOP_LOSS_LIMIT);
on a SELL the take-profit leg is the
aboveone, on a BUY it is thebelowone;the above leg's price must be strictly greater than the below leg's. Binance's full rule is
above > last traded price > below, and this server does not know the last traded price — only the relationship between the two prices you pass is checked here. Read the market withbinance_get_ticker_pricefirst.
When to Use:
Bracketing an open position with a target and a stop in one atomic request.
Any time two orders must be mutually exclusive — placing them separately risks both filling.
When NOT to Use:
For a single order —
binance_place_order(spot_orders.py).When the bracket should only arm after an entry fills — that is
binance_place_otoco_order.To change an existing list: cancel it with
binance_cancel_order_listand place a new one; there is no amend for lists.
Returns:
A confirmation echoing exactly what Binance returned: orderListId, contingencyType,
listStatusType, listOrderStatus, listClientOrderId, and a ### Legs table built from
orderReports when the response carries one (ids only otherwise, and it says so).
Nothing is inferred.
Examples: params = {"symbol": "BTCUSDT", "side": "SELL", "quantity": "0.001", "above_type": "LIMIT_MAKER", "above_price": "72000.00", "below_type": "STOP_LOSS_LIMIT", "below_price": "58000.00", "below_stop_price": "58500.00", "below_time_in_force": "GTC", "list_client_order_id": "btc-bracket-001"} params = {"symbol": "BTCUSDT", "side": "BUY", "quantity": "0.001", "above_type": "STOP_LOSS_LIMIT", "above_price": "71000.00", "above_stop_price": "70500.00", "above_time_in_force": "GTC", "below_type": "LIMIT_MAKER", "below_price": "60000.00"}
Error Handling:
Error: … trading is disabled …→ the kill-switch is off; nothing was sent.-2010 → insufficient balance, a symbol filter (LOT_SIZE / PRICE_FILTER / NOTIONAL), or the pair sits on the wrong side of the last traded price.
-2021 means a LIMIT_MAKER leg would have taken liquidity immediately.
-1013 / -1111 are precision / filter errors — read
binance_get_exchange_info.A 5xx or a timeout means the execution status is UNKNOWN — the list may well be live. Query it with
binance_get_order_list(bylist_client_order_idif you set one) before doing anything else. NEVER resend blindly.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |