Execution Infrastructure
One signal layer, two brokers, and a rule: code that decides is never code that's armed.
This page is the engineering story under the research: the execution stack we built to run the same strategy code on two independent venues — TradeStation through its order API, and Interactive Brokers through an unattended IB Gateway — with a shared order manager between the signals and either broker. There are no performance claims here; results, where they exist, live on the verification pages. The story is the plumbing.
2
Independent broker integrations
4
Pre-trade risk gates on every order
2
Keys to arm any executing session
0
Orders automation can send unarmed
Counts describe the code, not returns. Module names are given throughout so an auditor can ask for the exact files.
The shared layer
One order manager between every strategy and every broker.
Strategy code emits target portfolios. The order layer — paper_trading/order_manager.py gated by paper_trading/risk_controls.py — diffs targets against broker-reported positions and turns the delta into orders. Strategies never talk to a broker directly.
Idempotent placement, polled to a terminal state
Every order carries a unique client order id, so a retry after a timeout can never double-fill. Nothing is fire-and-forget: each placed order is polled until the broker reports a terminal state — filled, cancelled, rejected, or expired — and partial fills are reported as filled-vs-requested quantity, not assumed away.
Four pre-trade gates, checked before anything is sent
Single-name concentration cap, gross-exposure cap, a per-order notional sanity check that catches fat-finger deltas, and a drawdown kill-switch that halts all trading. The kill-switch peak is persisted to disk — a kill-switch that forgets its peak on restart is no kill-switch. A blocked order is logged with its reason and never reaches the broker.
Order-lifecycle hardening learned the hard way
DAY orders rejected after the 4:00 PM ET cutoff automatically re-submit as GTC (matching the broker's exact rejection text). End-of-day flattens are broker-verified in a loop — cancel working orders before replacing them, then escalate to marketable-limit orders until the account is confirmed flat, with a hard entry cutoff before the close. Session runners support bracket-style parent + take-profit + stop-loss groups on both venues.
Reconciliation against broker truth, not our own state
Positions are re-queried from the broker before each decision tick — the ledger never trusts its own memory of what it holds. Both venues write the same JSONL fire-event schema, and a nightly job diffs them: fire-count delta per sleeve (should be zero when signals are byte-equal), same-direction match rate, and a skip-reason breakdown for every miss.
Cross-venue audit: scripts/cross_broker_reconciliation.py — the nightly per-sleeve diff of both venues' ledgers (fire delta, match rate, skip reasons), built for the June-2026 mirror program and kept as the template for any future dual-venue run.
Two venues, one contract
The broker is a detail the strategy never sees.
Each venue keeps its own quirks behind the same interface. The IBKR side lives in paper_trading/ibkr_mirror/ (connection, paper-account enforcement, safety) with the gateway supervision under ops/ibkr/.
TradeStation — API order lifecycle
- REST v3 brokerage integration: position reads, order placement, and a status poller mapped to the broker's full terminal/working state codes.
- SIM routing enforced by construction — the execution layer refuses to run against a live-flagged account configuration.
- Dry-run is the default everywhere: runners compute and log intended orders; sending anything requires explicit flags plus the arming keys below.
- The same manager serves every strategy runner, so lifecycle fixes (the DAY→GTC fallback, the verified-flatten loop) land once and apply to all.
Interactive Brokers — unattended IB Gateway via IBC
- Headless IB Gateway (~80 MB, no GUI) supervised by launchd; IBC handles the login flow so no human touches it day-to-day.
- Auto-restart hardening: IBKR force-logs-out nightly, and the restart token must be written while the session is still authenticated. The restart was originally scheduled after the logout — a 14-hour outage taught us to move it before, with a buffer.
- A belt-and-suspenders watchdog probes the API port every morning after IBKR's reset window; if the gateway is down it restarts the service and emails the operator.
- Mirror processes verify the account is a paper account (DU-prefix check) on every connect and refuse to send orders otherwise.
The arming discipline
Decision code and execution rights are separated on purpose.
Every runner defaults to decision-only: it computes, logs what it would do, and sends nothing. Turning decisions into orders requires keys that only a human sets — and any path that could touch a real account stacks further interlocks on top (an explicit live flag, an account check against a frozen manifest, a wired-marker file, and a typed real-money phrase). The automation cannot arm itself.
Key 1 — an environment phrase
Each executing session must be started with an explicit acknowledgment phrase in its environment. No phrase, no orders — the process runs decision-only.
Key 2 — a fresh ack file
A human touches a per-sleeve approval file that expires after 24 hours. Execution rights decay daily by default; continuing is a deliberate act, not a standing state.
Always — the HALT file
One file path halts everything, checked before every order on both venues. Any process (or human) can set it; only a human removes it.
Field notes · execution costs
Passive fills select against you — we measured it.
A standing industry answer to execution cost is: stop crossing the spread — post a resting limit order and get paid the spread instead of paying it. We tested that claim against one of our own cost-marginal strategies with an honest fill model on real one-minute bars: a buy limit below the open counts as filled only if the market actually traded down through it, entry at the limit price exactly, and every unfilled signal is dropped as a missed trade.
The result was adverse selection, working exactly as the textbooks warn. The fill condition — price coming down through your bid — preferentially selects the names that kept falling. The reversions you most wanted bounced without ever touching the bid, so the misses were concentrated in the winners. Net effect: with fill rates of roughly 70–90% depending on how far the limit was posted, the passive arm did worse than simply crossing the spread, at every limit offset we tested.
The honest summary: passive execution converted an execution-cost problem into a selection problem, and the selection problem was bigger. Spread capture is real, but on a directional reversion signal it is outweighed by adverse selection plus the opportunity cost of missed fills.
Why this is on an infrastructure page
Because it changed how we build. Order-type choice is not a plumbing detail you optimize after the research — it is part of the strategy's edge accounting. Our cost tiers stay frozen at order-crossing assumptions, and no strategy gets promoted on the hope that "better execution" will rescue it.
Experiment: scripts/exec_alpha_gapfade.py (H-EXEC-GAPFADE-1) — deliberately qualitative here: no performance numbers on this page.
Status — what runs today
Built for two venues. Honest about what's live on them.
In use
- ✓ The TradeStation path executes the record-only challenge track — orders go to a SIM account only, while the real account receives decision-only logging with zero orders.
- ✓ The order manager, risk gates, HALT file, and arming keys gate that SIM execution daily.
- ✓ The reconciliation tooling and ledger schema remain the standard for every record-only shadow we publish.
Retired / idle
- – The dual-broker mirror program (June 2026) is retired: the sleeves it mirrored were superseded by later verification waves. See Hall of Retired and Track Record.
- – The IBKR paper account is flat and idle; the mirror runners are decommissioned.
- – No live-capital performance exists or is claimed — here or anywhere on this site.
The research this stack exists to serve — and the verified record it reports into — live on the verification pages.
How we research