When to Use
Invoke this skill when deploying algorithmic strategies across broker APIs whose
native order-type support differs. IBKR exposes bracket orders, OCA groups, iceberg
via displaySize and the TWAP/VWAP IBALGOs; Alpaca exposes bracket and OCO order
classes but no iceberg and no execution algos; Zerodha withdrew bracket orders but
offers a native iceberg variety; Binance Spot covers OCO, OTOCO and icebergQty but
reaches TWAP only through a separate Algo API. Submitting an unsupported type gets it
rejected. This skill checks native support first and, where it is missing,
decomposes the order into a primary leg plus explicitly typed legs for a local
Execution Management System.
The two things it must get right, because everything downstream trusts them: the quantity conservation (does the plan execute exactly the requested size?) and the price geometry (are the protective legs on the side of the market that protects?).
When NOT to Use
- As an EMS.
scripts/capability_matrix.pyplans an order. It performs no network I/O, submits nothing, watches no quotes and holds no state. The trigger watching, the timers, the sibling cancellation and the persistence are all yours. - As a live source of broker truth.
DEFAULT_CAPABILITIESis a dated, sourced template. Support changes and varies by asset class, product and entitlement — re-verify againstreferences/standards.md's links before trading it. - For VWAP, pegged, trailing-stop or auction orders the broker lacks. These are refused, not approximated. Emulating VWAP as evenly spaced slices yields a TWAP benchmarked against the wrong number. See the Related Skills.
- As a substitute for idempotency. The plan tells you to fire one primary order;
making sure a retry does not fire it twice is
order-placement-idempotency. - Where a native path exists. An emulated OCO is strictly worse than the venue's own: local triggers add latency, a failure domain, and real double-execution risk.
Prerequisites
- Each target broker's documented order-type surface — the API reference, not the
four types you happened to test.
references/standards.mdlists them with sources. - A local EMS that can watch Level 1 quotes, run interval timers, cancel a sibling leg on trigger, and persist emulated legs across a restart.
- The venue's minimum order size and quantity step per instrument, for
min_slice_qtyand for rounding the slice schedule before dispatch.
Workflow
-
Register capability profiles, and make them self-consistent. The
supports_*booleans are a view ofnative_order_types, not a second switch — a profile where they disagree raises at construction rather than reaching order time claiming native OCO while silently taking the emulation path. -
Validate before deciding how to route.
plan_order_executionchecks quantity, prices, leg completeness and price geometry ahead of the native/emulated branch. A bracket with no exit legs, or with the stop on the profitable side, is malformed regardless of whether the broker would have accepted it — the native path is not a validation bypass. Arguments the requested type does not consume are refused by name rather than discarded: astop_loss_pricesilently dropped from a MARKET order leaves the caller believing a position is protected that nothing is watching. -
Read
actionper order type — they are not the same. ForBRACKETit is the entry side and the exits invert it. ForOCOit is the side of both exit legs, matching the same-side constraint Binance and Alpaca document for their native OCOs.BRACKET+BUYtherefore wants the target above the stop, andOCO+BUY(closing a short) wants it below. -
Execute the plan exactly as written. If
has_primary_order, fireprimary_quantityofprimary_order_typeatprimary_priceonce; then registeremulated_legs.primary_quantityplus every scheduling leg's quantity already equals the requested quantity — do not re-slice the parent. Whenprimary_order_typeisNone(emulated OCO), fire nothing now. -
Persist before you submit.
plan.to_dict()serializes the plan losslessly. Write it down before the primary order goes out, not after.
Full procedure, EMS contract and the price-geometry table: see
references/workflows.md. Per-broker capability evidence with sources: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
- Firing the primary slice and a feeder sized for the whole parent order. If the
feeder's quantity is the full parent quantity while the primary is already the first
slice, an EMS following the contract executes
quantity + one slice— a 100-unit iceberg in 3 slices sends 133. The feeder here carries only what the primary did not. - Slicing in binary float. Ten thousand units in seven slices, summed, is not ten thousand units. The residue is tiny and the consequences are not: the final slice can be rejected for exceeding the parent's remaining quantity, or leave dust.
- Assuming emulated OCO atomicity. One leg can fill on the exchange while the
local cancel of the sibling is still in flight — both execute. Even IBKR's native
OCA only removes this risk with an
ocaType"with block", which routes one order at a time;ocaType=3carries the same exposure a local emulation does. - Naming the unsupported type as the primary order. An emulated OCO whose plan
reports
primary_order_type=OCOsends the caller back to the exact endpoint that just failed the native-support check. - Exit legs on the wrong side of the market. A "protective" stop above a long entry is already through its trigger when the EMS registers it — it does not protect the position, it closes it instantly at a loss.
- Falsy-checking a price.
if not stop_loss_pricereads0as "not supplied" and plans a bracket with a silently missing leg instead of rejecting a bad price. - Iceberg slices below the venue floor. Zerodha's iceberg takes 2–50 legs; Binance
enforces
LOT_SIZEand a minimum notional. A slice below the floor is a guaranteed rejection — passmin_slice_qtyso the plan fails at planning time. - Slicing a market order and calling it an iceberg. An iceberg is a resting limit order with a restricted display size; market slices sweep the book instead. This is why the venues offering it natively attach it to limit orders.
- Treating "native TWAP" as unconditional. Binance's TWAP lives only on the Algo endpoints, bounded by duration and notional; IBKR's TWAP/VWAP IBALGOs are documented for US equities. Both are "native" and neither is universal.
- Losing emulated state. If the EMS restarts without its legs, the emulated stops never fire and nothing raises an error — the position is simply unprotected.
Verification
- Run the unit suite and confirm every test passes:
python -m unittest discover -s skills/broker-order-type-capability-matrix/scripts - Assert quantity conservation for an indivisible size: plan a 1000-unit TWAP in 7
slices and check
primary_quantity + sum(metadata["slice_schedule"])equals exactly 1000. This is the highest-value single assertion in the suite. - Assert an emulated OCO reports
primary_order_type is Noneandhas_primary_order is False. - Assert an inverted bracket (stop above target on a long) raises on the native path too, not just the emulated one.
- Assert
BRACKET+BUYandOCO+BUYaccept opposite price orderings. - Assert a
0price is rejected rather than treated as absent. - Assert
min_slice_qtyrejects a schedule whose slices fall below the venue floor. - Assert
BrokerOrderCapabilityMatrix(custom_matrix={})resolves no brokers at all. - Spot-check each profile in
references/standards.mdagainst the broker's live documentation before promoting to live trading.