An event-driven trading research platform for equities and FX, written in three
languages on purpose: Python for research and the reference implementation, C++ for
the latency-critical path, Rust for the hard risk engine and infrastructure (the Java
strategy layer is archived, frozen at v1.14.0 and retired from the release in v1.17).
Market data → order books → 205-feature factory →
24 alphas → validation → ML → portfolio → fail-closed risk → execution/SOR →
deterministic replay → TCA → decision trace → 7-state promotion lifecycle. Cross-language
golden tests hold it together, and one command (python -m iap.mvp run) runs
the whole loop on one instrument. Its golden result loses money, and the page says so.
Seven real Nasdaq ITCH sessions and a two-day 2026 holdout have been studied. The signals are statistically real; none pays its trading costs. Nothing is claimed to be profitable.
A research platform built to make self-deception hard: one seeded
generator, integer prices and timestamps, byte-identical goldens across three languages,
a ledger that counts the looks recorded through it and raises the significance bar with
it (a lower bound on the search, not a complete history: research/v118/LEDGER_INVENTORY.md), and pre-registration checked by code and anchored in git. The loop from market data
to TCA runs end to end in Python (python -m iap.mvp), and the Python
iap.metrics process is what the deployment scrapes.
A profitable strategy: no alpha has been promoted. Not connected to a venue: there is no live order gateway, so the risk checks guard a simulator. Not a large-sample study: the real data is nine Nasdaq sessions on AAPL, MSFT and QQQ, enough to show a signal exists and too little to size it. Not four production services: the Java tree is an archived copy frozen at v1.14.0, not built and not a port target; only its risk engine is kept as the byte-identical mirror of the Rust one.
Each one owns what it is good at, and the duplication is the test. Python defines the semantics; C++ and Rust must reproduce them from the same golden files. Parity has caught real bugs (a Rust feature engine reading a half-built book) and missed others (three risk engines agreeing on a wrong answer). Both stories are in LEARN.md §21 and §36.
| Question | Answer | Where |
|---|---|---|
| Does any alpha pass every gate on the bundled data? | No. 0 PROMOTE of 24; 18 never forecast a move larger than their cost, and the 6 that trade lose. | REPORT |
| Are the signals real on real data? | Statistically, yes: EQ01 microprice IC 0.105, t 21 (v1.13 statistics, before the v1.14 day-separated HAC, which will lower the t). | REAL_DATA §3.1 |
| Do they pay? | No. A 1 s edge of ~0.07 bp against a ~0.7 bp taker round trip. | LEARN §31 |
| Do they hold out of time? | 4 of 4 confirmatory signals confirmed on two 2026 sessions, pre-registered first. Their size is not established, and those two days are spent. | REAL_DATA §3.2 |
| Does the closing-cross strategy work? | No demonstrated edge: +19.4 bp, 95% CI −254 to +293 bp, from 3 trades. Underpowered, not negative. Holdout not confirmed, 0 trades. | LEARN §42 |
| Can a maker do better than a taker? | No, not on real data (v1.16). Taker capacity: 0 of 45 cells break even. Two-sided quoting v2 (in-sample, 6 test sessions): every cell loses $5–7k per session, adverse selection about 4x the spread captured; skewing with EQ01/EQ05 cuts the loss by about $750–920 per session, so the signals help execution, not a standalone maker. Synthetic maker backtest: −6.7 bp per trip. | ROADMAP §3.8 |
| What does the full loop do? | Loses 82.84 USD on its golden session. | MVP |
sor +5.1 ± 1.3 bps and 8 pp lower fill than pinned Nasdaq; sor_sweep +3.8 ± 2.1 bps; 2 day clusters, provisional. The pre-declared six-day out-of-sample run is also negative: sor +2.2 / +1.1 bps, sor_sweep +0.6 / +1.2 bps (AAPL / MSFT), no interval excludes zero.v2_v113.main; the look ledger is a hash chain; supply-chain audits block the build.No latency edge; benchmarks are not colocation numbers. Every latency or speed figure below was measured in-process on a shared CI runner or a small container with steady_clock and no CPU pinning: no network, NIC, kernel bypass or venue round trip. The signals are execution inputs, not strategies: B7 saved no cell the 2 bp minimum, the net-of-cost screen passes 0 of 22, and the v1.16 monetisation study found no taker, capacity, quoting or DeepLOB edge. Real-data and synthetic-data looks are counted separately below.
research/experiments.json)--engine rustPOLYGLOT-OVERRIDE:v4 bundle, report-onlyequities.flow.calibration = "legacy_budget") and
the reports, the ledger, the lifecycle registry and four goldens were regenerated in one CI
job. The equity statistics are weaker on the corrected data: one alpha fell from ITERATE to
REJECT, EQ03's uncrossed IC went from 0.0298 (t 10.57) to 0.0190 (t 5.78), and the power
study detects less than it did. The ML gate now passes and the tree models and the MLP were
fitted; none earns its costs. The MVP session trades more and loses more. The looks spent on
the old dataset stay in the multiple-testing ledger. Still 0 PROMOTE; details in the
changelog.
--methods legacy_v1 reproduces the v1.4.0
report). Same dataset, new rules: the gate t is the HAC t of the pooled slope and the
PROMOTE threshold is the ledger's multiple-testing threshold (4.365 instead of 3.0), so
three alphas are significant where six were; the cost-aware backtest shows 18 of the 24
alphas never forecasting a move larger than their round-trip cost, so they make no trade,
and the six that trade lose 22 to 733 USD where the old sign policy lost five or six
figures; verdicts are 0 PROMOTE / 11 ITERATE / 13 REJECT (FX03 down, FX10 and FX11 up);
EQ11's gate IC falls from 0.0261 to 0.0038 once blackout rows are scored. The MVP's
events, fills and P&L are identical. Nothing was tuned to recover a v1.4.0 conclusion.
Still 0 PROMOTE; details in the
changelog.
v3 method bundle samples days by seed
and stratum and reports with and without event days, clusters inference by day (and bootstraps
whole days), cuts folds at session starts, gates on the equal-weight per-instrument IC, labels
ITCH-only results honestly as one Nasdaq book, and bounds label freshness causally. The feature
build runs one day per process (--workers N, byte-identical, 5.1x on 5 synthetic days).
And because a taker cannot pay ~0.7 bp for a ~0.07 bp edge, the platform now has the maker side:
a simulator calibrated from the event stream, a backtest that posts at the touch, fills by queue
position, earns the rebate and is charged its measured markout, with an optional passive exit.
On synthetic data it still loses (−6.7 bp per trip with the passive exit, −11.4 bp crossing out);
the real-session maker run comes next. Every default and published number is unchanged.
v4 bundle, report-only; the paper's
worked example reproduces at DSR 0.9004). A C++ tick-to-trade benchmark times every event from
a decoded frame to an order decision: p50 1,279 ns and p99 2,559 ns on the golden vector on a
CI runner (not colocation), with the boundary stated (no network, no kernel bypass, no C++ risk engine). Also
opt-in and Python-only: 19 extended features with event and volume clocks, a cost-aware
multi-venue router (capability only: the real data is one Nasdaq book), and a fill-hazard model
with post-only posting, hazard-driven reprices and markout feedback. Ops: page / ticket /
watchdog alert routing with receiver URLs from Secret files, latency burn-rate SLOs with
promtool tests, and a state-backup job. The first real-data study result is in: the
pre-registered closing-cross strategy AUC01, exploratory and in-sample, shows
no demonstrated edge (session-clustered mean +19.4 bp from 3 trades on 2 sessions, 95% CI −254 to +293 bp): an
underpowered result, not a negative one. Its 2026 holdout and the maker and quoting studies
were then running (outcomes in the v1.13 box).
governance CI job that verifies the blackboard and checks ledgers only grow.
The quoting study was aborted before any result was read: its registered
Avellaneda–Stoikov parameters were numerically inert (every quote a fixed 1–2 ticks behind the
touch), so it could not test skew against no skew; a tick-based protocol will be
pre-registered. The maker study is still running. AUC01's 2026 holdout:
not confirmed, 0 trades. v1.14, the correctness release that regenerates goldens and reports
once, has its own box above.
emi.nasdaq.com (no terms were shown on the directory as of 2026-10-11). Raw
and normalized data and feature stores are never committed or redistributed; only aggregate
derived statistics that cannot reconstruct the data are published. Commercial use would need a
Nasdaq data agreement
(REAL_DATA §2.1).
Algorithms and the SOR judged on implementation shortfall (B2–B6: benchmark, quote fading, queue-aware cancels and related rules). B1 depth-50 finding (in-sample): at 0.5 % ADV the old 10-level walk cap caused the depth exhaustion (60–67 % of orders, 0 % with 50 levels); at 2 % ADV the displayed book itself runs out; costs rise 2–5 bps at 1–2 % ADV, so depth-10 understated cost. B10 dark venue: modelled, not measured. DEPTH_COMPARISON_multi7, EXECUTION_QUALITY
A cross-sectional breadth book behind a net-of-cost screen. Result: 0 pass / 22 fail / 1 not screened of 23 candidates; net IC negative in every screened row. The book was registered under option (a), so it makes no confirmatory look and the holdout stays unread. SCREEN_multi7, PORTFOLIO_BREADTH
IEX HIST is a free, daily second venue. 9 overlap days approved; day 1 (2025-11-28) is processed: 148,115,121 IEX-TP messages, 0 sequence gaps, and the TOPS oracle shows 0 mismatches for AAPL, MSFT and QQQ. The other 8 days are for v1.17.1. IEX_OVERLAP_SET
E1 spend ledger with a monthly cap of USD 30; E2 research assistant; E3 morning ops and E4 docs sync on a smaller model. The LLM never computes a number, signs, registers or merges. AGENTS
A guided explanation for a newcomer: the pipeline on one page, how a signal is researched and judged, how an order becomes fills, the risk engine that says no, what the ML layer showed, and where the line for LLMs and agents is drawn. Every section ends with where to look and a command that runs. HOW_IT_WORKS
The rule was pinned first: LLM reasoning is never on the trading path, is read-only, and cannot override risk or send an order. The governance an agent has to pass through came next (v1.7–v1.10): a write broker over a hash-chained blackboard, costed and code-bound pre-registration, a hidden-seed reserve, signed human approvals, a read-only MCP server, git anchoring and Ed25519 agent signatures. Since v1.11 one optional component calls a model, the research agent, and only when an operator runs it with a key; no test or CI job does. Since v1.13 it may request only pinned model ids and its numbers rule is stricter. The page says what would be theatre on this data. HOW_IT_WORKS §6
Corrected statistics and policies, added opt-in in v1.3.0 and the defaults since v1.5.0, each with its old rule selectable under a legacy name: a pooled-slope HAC t, a ledger-derived threshold, a cost-aware position policy, a recompute leakage probe, per-fold diagnostics (report-only). A planted-signal study measures what the validation chain can detect — and shows it promotes nothing. RESEARCH_VALIDITY, POWER_REPORT
Stated plainly, with a backlog epic for each gap: real exchange data at scale (ITCH 5.0 / LOBSTER ingestion exists; v1.6.0 ran all alphas on 7 real Nasdaq days — real but untradable signals, no trade survives costs; v1.8.0 confirmed four of them out of time), measured tick-to-trade latency, a simulator calibrated to live fills, book-level risk, research throughput. Nothing here is a claim about a market. ARCHITECTURE §14
A Claude model with eight research tools and nothing else: propose, pre-register (signed
by a key the model never sees, one look each, capped per session), run a gated study on an
allowed dataset, read a report, file a finding. Code computes every metric and the verdict;
a finding is filed only if every number in it appears in a cited artefact. Budgets cap
spend, tokens, tool calls and pre-registrations; the transcript is kept. Optional
[llm] extra; mocked evals in CI
(GOVERNANCE §2b).
Fitted models as citable records: the id is a content hash of the artefact, data version and dates, features, code fingerprint and seed; records are immutable and re-verified on load; registration needs a pre-registration or is marked exploratory. Feature, prediction and calibration drift (PSI, KS, ECE, Brier, IC decay) as OK/ALERT, and a candidate scored beside the champion that never acts (API_ADAPTIVE §9).
Every component that exists in more than one language has one decision per copy:
canonical (new behaviour lands here) or frozen (built, tested, golden-pinned, no new
features). 18 canonical, 20 frozen, nothing deleted. A CI gate fails a pull request that
changes a frozen copy without a POLYGLOT-OVERRIDE: line, and one that adds an
API without new-api
(POLYGLOT).
Seeded synthetic equity MBO + multi-venue FX quote generator (regime-switching efficient price, clustered flow, halts/auctions, injected anomalies), raw→normalized pipeline with sequence-gap/duplicate/out-of-order QC, JSONL + IAP1 binary codecs byte-identical across languages, Parquet research datasets.
L1/L2/MBO reconstruction with FIFO queues, pinned modify semantics, marketable crossing ADDs, snapshot recovery (incl. broken-burst rule), per-venue + consolidated views, deterministic checkpoints — identical state in Python, C++, Rust and Java after every event.
205 registered features across price, microstructure, order flow (OFI L1–L10),
liquidity, volatility, time-of-day, cross-asset, venue, regime and execution
families; event-driven incremental engines in all four languages validated to 1e-9
against golden checkpoints. Since v1.11 the Rust engine is callable from Python
(pyo3, one abi3 wheel, opt-in --engine rust) for the 45 native slots;
its row-by-row comparison with Python found and fixed a snapshot-burst bug the
checkpoints could not see.
12 equity + 12 FX flagship alphas (microprice, multi-level OFI, queue depletion, momentum/reversal, VWAP deviation, lead-lag, carry-style currency factors, regime-conditioned variants), each with a mandatory economic-rationale docstring and declared feature dependencies.
Event-time labels at 11 horizons (10ms–15m), walk-forward with purging + embargo,
automatic leakage tests incl. a recompute-from-raw-events probe, IC/RankIC/pooled-slope HAC t/hit/decay/turnover/edge-breakeven capacity,
a persistent multiple-testing ledger, and cost/latency/regime stress — the §20
promotion gates, enforced literally. The opt-in v3 bundle (v1.9) adds
day-aligned folds, day-clustered and day-block-bootstrap t, an FOMC/holiday-excluded
block and the equal-weight per-instrument gate IC.
Linear baselines gate the advanced models, literally: trees and the MLP run only if the best linear model shows OOS signal against the frictionless label. On the bundled data it does, barely (ridge, pooled OOS IC +0.0081), so xgboost, lightgbm and the MLP were fitted — and no model earns its costs. A calibrated trade/no-trade meta-label gate, evaluated economically, takes zero trades and is reported as degenerate rather than as a success. Reproducibility manifests (data/feature/model versions, windows, hardware) on every fit.
alpha'w − λ·w'Σw − tc(Δw) under position/gross/net/participation/turnover/ vol-target and FX currency-exposure constraints; deterministic projected-gradient production optimizer (Java) matching the Python reference to 1e-9, with a constraint-audit report.
Fat-finger, price-band, stale-price, position/notional/gross/net, loss limits, rate throttle, duplicate-order and self-match prevention, sequence-gap gating, venue-disconnect handling and four-scope kill switches — Rust reference with Java and Python ports, byte-identical audit logs, replayable. Since v1.3.0 also closed against future-stamped marks, NaN, integer overflow and router-bound orders under a venue kill, with an edge golden all three engines replay. Since v1.14 the audit log is a SHA-256 hash chain that survives restores, and SEC 15c3-5 pre-trade checks 23-29 run after the legacy set, so no existing rule id or golden decision moved.
Market/limit/IOC/FOK, VWAP/TWAP/POV/IS parent algos, deterministic queue-position modeling for passive orders (pinned depletion rules), fees/rebates/impact, venue latency with seeded jitter, and fee-aware smart order routing — C++ reference fills reproduced exactly by Java and Python. The simulator never fills more than the market displayed or traded. Opt-in since v1.10 (Python only): an Almgren–Chriss IS trajectory and efficient frontier, alpha-driven urgency, and a VWAP on a forecast volume curve. Since v1.13, opt-in SEC Section 31 and FINRA TAF fees (default 0).
A versioned calibration estimated from the event stream (fill rates, queue-depletion hazards, latency, maker markouts, impact) feeds the simulator. A maker backtest posts at the touch behind a gate on expected edge versus measured adverse selection, with a taker or passive exit; a two-sided quoter skews an Avellaneda–Stoikov reservation price with the alpha. Both decompose P&L exactly; since v1.13 quotes can be set in ticks and markouts use one reference convention. Python research code, opt-in; results so far are synthetic (API_TRADING §2.6–2.8).
Nasdaq NOII and cross messages decoded in a separate opt-in pass, so the normalized dataset and its version do not change. Auction features (imbalance ratio, reference-price drift, far/near spread, time to cross), cross targets, and a closing-cross strategy with a purged, day-aligned walk-forward. The CLI refuses to backtest without a pre-registration. First real-data result (v1.12, exploratory, in-sample, 7 sessions): no demonstrated edge on the closing or the opening cross, with only 21 decisions per cell; the 2026 holdout was not confirmed (0 trades), and since v1.13 an exit at or after the MOC entry cutoff is priced as a taker trade (REAL_DATA §3.3).
Combinatorial purged cross-validation (all C(N,k) splits, reassembled into backtest paths,
day-aligned), the probability of backtest overfitting by CSCV, and the probabilistic and
deflated Sharpe ratios with a minimum track record length. The trial count comes from the
ledger's distinct configurations, with the DSR at the raw look count printed beside it. A
helper puts a DSR block next to any study's per-day P&L without touching its verdict
rule. Opt-in v4, report-only
(RESEARCH_VALIDITY §1b).
A discrete-time (cloglog) fill-hazard model fitted on maker labels and versioned as
iap.fill_hazard/1; post-only orders that reject or slide instead of taking;
entry reprices that give up when the hazard says a repost will not fill; and markout
feedback that stands down, widens or shrinks while fills are toxic (synthetic quoting
markout −62.75 → −1.75 with a 2 bp threshold). A cost-aware multi-venue router prices fill
probability, toxicity, latency and fee tiers. Python, opt-in, synthetic only
(API_TRADING §2.9–2.10).
Nineteen opt-in features: queue time-to-depletion, trade-sign autocorrelation, Hawkes intensities with online recursion, and odd-lot / non-displayed execution shares, with rows sampled on an event or volume clock. They carry their own feature version; the default registry hash and every golden are unchanged. Not yet run on real data (API_FEATURES §8).
Alertmanager routes page, ticket and a Watchdog
heartbeat, with receiver URLs read from Secret files (nothing secret committed) and a local
echo sink for compose. Multi-window burn-rate SLOs on the existing JVM latency histograms
(99% under ~1.05 ms over 30 days) with promtool tests, and a state-backup CronJob. HA is
one pod restarting and resuming from its checkpoint, not active-active
(RUNBOOK_alerting).
Every agent write goes through one broker onto a hash-chained blackboard. A
pre-registration debits a look on the multiple-testing ledger and records a fingerprint
of the alpha's code, features and pinned dependencies; the gate refuses a run if any
changed. verify-board checks the chain, that each committed version is a
prefix of the current one, the git anchors, and every Ed25519 signature. Since v1.13 a
required governance CI job runs it on every change, with checks that every
signer has a registered key and that ledgers and the board only grow; human lifecycle
edges need a signed approval. Since v1.14 the agent key registry must be signed by a
maintainer trust root read from main, the look ledger is itself a hash chain,
and pip-audit, cargo audit, cargo deny and Trivy block the build
(GOVERNANCE).
Replays every market event in event-time through book → features → alphas → portfolio → risk → execution with modeled decision/wire/venue latency, partial fills and queue position; identity-tested P&L accounting; checkpoint/restart bit-identical to uninterrupted runs.
Arrival/VWAP/TWAP benchmarks, Perold implementation-shortfall decomposition (delay/trading/opportunity — exact identity), spread/impact/timing split, post-fill adverse-selection markouts at 100ms/1s/10s, and impact regression versus participation.
Models decay, so the platform measures it: pinned PSI/KS drift monitors against
serialized research baselines, rolling realized-vs-research IC, drift-triggered
refits on purged/embargoed windows, and an IC-gated ACTIVE→WATCH→RETIRED state
machine with hysteresis and a CUSUM retirement rule (the legacy consecutive-breach rule stays selectable). In the adaptive backtest RETIRED verifiably halts the
allocation; in the live paper loop the gauge is deliberately observational —
the AlphaLifecycleRetired alert pages a human, nothing reduces the position
automatically (API_ADAPTIVE §6, PLATFORM_CONVENTIONS §12.6). Live PSI,
rolling-IC and lifecycle gauges are scraped in paper trading.
The Python reference generates validated golden vectors (events, book states,
codec SHA-256, features, alphas, risk decisions, fills, portfolio); C++, Rust and
Java must reproduce every one. tests/harness/run_all.sh prints the
parity table in one command. Agreement is not correctness: four engines can agree on
a wrong rule, and engines that agree at every checkpoint can differ between them.
17 JSON Schemas mirrored by 22 frozen, validated Python contracts and 18 runtime-checkable Protocols; canonical JSON reproduced byte for byte in four languages. Python ports of the hard risk engine and the execution stack proven by the same goldens the Java ports use (API_CONTRACTS, API_TRADING).
RESEARCH → CANDIDATE → VALIDATING → PAPER → ACTIVE ⇄ WATCH → RETIRED: 17 pinned edges, 20 gates reading typed evidence, a byte-deterministic registry and transition log, ports in Java and Rust from one golden. Bootstrapped on the bundled research: 23 CANDIDATE, 0 beyond.
One validated record per decision — signal, portfolio, risk, orders, routing, fills, TCA,
attribution — as canonical JSONL with a stream digest that an incident replay must
reproduce, indexed by a SQLite/PostgreSQL-portable store (24 tables, 3 views) and rendered
by explain() identically in Python, Java, Rust and C++.
python -m iap.mvp run: seeded feed → books → features → EQ01/EQ03/EQ06 →
portfolio → hard risk → TWAP/POV/IS → SOR over three venues → simulator → TCA → trace →
SQLite → report, deterministic twice over. The golden run loses 82.84 USD, the realized-IC
gap is attributed to the data rather than a leak, and the document says so.
Prometheus metrics from the Python iap.metrics process (/metrics, /health,
/ready, /status; 30 live metric families read from the MVP run's artefacts and the live
risk limits, every retired Java-only name exposed as _available 0),
recording + alert rules with promtool unit tests, two Grafana dashboards, structured
JSONL logs, three Dockerfiles (python, cpp, rust) + docker-compose + Kubernetes
manifests pinned by digest at release, runbooks, governance and
reproducibility docs, and a paper-trading mode. Alertmanager routes the alerts
by severity (page / ticket / watchdog); real delivery needs operator-supplied
page_url and ticket_url Secret files, and compose echoes to a
local sink until then. NetworkPolicies default-deny ingress
and egress.
Six case studies written from the platform's own measured outputs: OFI predictability, microprice/queue dynamics in FX, cross-venue lead-lag, alpha decay vs latency economics, queue-aware execution and adverse selection, and C++ vs Rust vs Java for event-driven trading.
Benchmark suite with published methodology (hardware, toolchain, boundaries, variance caveats — no isolated numbers): codec, book, replay, feature engine, alpha scoring and execution-sim stages, plus demo-scale Rust/Java replay throughput. Since v1.12 a tick-to-trade benchmark reports per-event p50 / p99 / p99.9 / max latency end to end and per stage (decode 271, book 83, features 735, alpha 135, decision 30 ns at p50 on the golden vector, shared CI runner, not colocation), guarded in CI at 8× the baseline.
The end-to-end quantitative finance portfolio this platform sits alongside: 10 project areas built twice each (equity + FX) in Python with C++ and Rust performance twins — 31 sub-projects, 6,446 tests, golden-vector cross-language validation, a 481-question learning guide and a 63-recipe cookbook. GitHub →
A zero-dependency quantitative finance platform for the JVM — research, validation, execution and connectivity, from walk-forward backtesting to a measured sub-microsecond tick-to-order path, with 1,090+ tests and CI on every push. GitHub →
git clone https://github.com/AshJha0/intraday-alpha-platform && cd intraday-alpha-platform # generate the deterministic synthetic dataset, then the feature/label store cd python && PYTHONPATH=src python3 -m iap.marketdata && PYTHONPATH=src python3 -m iap.features && cd .. # run everything — builds and tests python, cpp and rust, prints the parity table bash tests/harness/run_all.sh # the whole trading loop on one instrument, fully traced; then replay it from its capture cd python && PYTHONPATH=src python3 -m iap.mvp run && PYTHONPATH=src python3 -m iap.mvp replay --run ../data/mvp/58a10f2194a3c81c && cd .. # research pipelines (honest reports in research/) PYTHONPATH=python/src python3 research/alpha_reports/run_all.py PYTHONPATH=python/src python3 research/ml_reports/run_ml.py