Skip to content

Broker Order Type Capability Matrix

broker-order-type-capability-matrixsource

Use when one strategy runs across brokers with different native order types (bracket, OCO, trailing stop, iceberg, peg, TWAP) and must decide whether to send a native type or emulate it locally while conserving the parent quantity.

Version
3.0.0
Reading
6 min
Hands off to
6
Handed off from
4
License
Apache-2.0
CoversInteractive Brokers TWS APIAlpaca Trading APIZerodha Kite ConnectBinance Spot API

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.py plans 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_CAPABILITIES is a dated, sourced template. Support changes and varies by asset class, product and entitlement — re-verify against references/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.md lists 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_qty and for rounding the slice schedule before dispatch.

Workflow

  1. Register capability profiles, and make them self-consistent. The supports_* booleans are a view of native_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.

  2. Validate before deciding how to route. plan_order_execution checks 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: a stop_loss_price silently dropped from a MARKET order leaves the caller believing a position is protected that nothing is watching.

  3. Read action per order type — they are not the same. For BRACKET it is the entry side and the exits invert it. For OCO it is the side of both exit legs, matching the same-side constraint Binance and Alpaca document for their native OCOs. BRACKET+BUY therefore wants the target above the stop, and OCO+BUY (closing a short) wants it below.

  4. Execute the plan exactly as written. If has_primary_order, fire primary_quantity of primary_order_type at primary_price once; then register emulated_legs. primary_quantity plus every scheduling leg's quantity already equals the requested quantity — do not re-slice the parent. When primary_order_type is None (emulated OCO), fire nothing now.

  5. 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: see references/standards.md. Printable pre-flight checklist: see assets/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=3 carries the same exposure a local emulation does.
  • Naming the unsupported type as the primary order. An emulated OCO whose plan reports primary_order_type=OCO sends 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_price reads 0 as "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_SIZE and a minimum notional. A slice below the floor is a guaranteed rejection — pass min_slice_qty so 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 None and has_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+BUY and OCO+BUY accept opposite price orderings.
  • Assert a 0 price is rejected rather than treated as absent.
  • Assert min_slice_qty rejects 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.md against the broker's live documentation before promoting to live trading.

Verify it, from the repository root

python -m unittest discover -s skills/broker-order-type-capability-matrix/scripts

Hands off to 6

Skills this document names, usually in When NOT to Use, as the owner of a case it excludes.

Handed off from 4

Skills that name this one as the place a case belongs. The reverse edges of the graph.