atlas·exchange-in-a-box
market data → book → decision → order, measured honestly

An exchange in a box,
with the receipts.

ITCH 5.0 codecs, five order-book layouts, a deterministic matching engine, multicast market data with recovery, an OUCH order-entry session — and three measurements the public literature was missing. Built in modern C++ as a working textbook.

5.0µstick-to-trade p50 — 92% of it is the send syscall; the pipeline is ~400 ns
82.0%of real order flow within 5 ticks of the touch
65.6Mreal ITCH frames replayed, 0 decode failures

Personal learning project, built on personal time and personal hardware, using only publicly available specifications and publicly available data. Not affiliated with, endorsed by, or derived from any employer, past or present. Sources: docs/SOURCES.md.

the subject itself: a limit order book as a dot matrix — adds twinkle in, cancels fade, trades spark across the spread
three claims, three experiments

What got measured that wasn't public before

CLAIM 1 · order books

The workload is the result

Five layouts — std::map, sorted vector, flat array, 3-level bitmap, intrusive treap — under the literature's uniform-random prices and replayed real ITCH. The ranking does not survive the trip.

3.7× → 1.1×, tails invert
CLAIM 2 · kernel I/O

Latency under burst, not steady state

read() vs recvmmsg() vs io_uring vs AF_XDP, driven by the real 09:30:00 microburst (928k messages, peak bin 3.5× the mean): the burst triples the median, tails double, recovery takes ~2 ms — and the sender-side accounting catches every measurement trap steady-state means hide.

3.4 → 11 µs in the burst
CLAIM 3 · tick-to-trade

An honest end-to-end budget

Arrival → parse → book → decision → encode → send, attributed per stage in nanoseconds on hardware anyone can rent, with the instrumentation cost itself measured and subtracted.

5.0 µs, itemised
claim 1, live

Flip the workload, watch the ranking move

Same five data structures, same harness, same machine. The only variable is whether the prices look like a textbook benchmark or like Nasdaq at 09:30.

per-operation p50 (bar) and p99.9 (whisker), lower is better

Numbers are medians of 5 pinned-core repetitions; full distributions, hardware counters, and the committed result files are in the paper. Data: Nasdaq's public 2019-12-30 sample day.

the system

One data path, every stage measurable

The exchange side (matching engine, market-data publisher, order gateway) and the participant side (feed handler, books, strategy) — connected by the same wire formats the real venue uses, each stage provable on its own.

the other deliverable

The code is a textbook

Written for a strong Java/Python engineer who is a beginner in C++. Every non-obvious construct carries a // C++ NOTE: explaining what breaks without it and what the JVM analogue hides. The primer grows one chapter per concept, in the order the code introduces it — with real disassembly from this repo's own binaries.

strict aliasing & the memcpy idiom templates vs erasure constexpr tables in .rodata std::span & views value types RAII concepts atomics & memory orders false sharing the naive version, kept forever
no trust required

Reproduce it

Every number traces to a committed result file and the exact command that made it. The data comes from Nasdaq's own public sample server.

# clone, build, test (g++-12, cmake ≥ 3.22)
git clone https://github.com/Abhishek-Aditya-bs/atlas && cd atlas
cmake --preset release && cmake --build --preset release && ctest --preset release

# fetch the public sample day and cut the opening window
scripts/fetch-data.sh
zcat data/itch/12302019.NASDAQ_ITCH50.gz | build/release/bench/itch_slice 09:25:00 10:30:00 \
  > data/itch/rth-open.itch50

# claim 1: five layouts × two workloads, pinned, 5 reps + perf counters
scripts/bench_book.sh data/itch/rth-open.itch50 QQQ

# claim 2: the 09:30 microburst against each receiver backend
build/release/bench/burst_bench run recvmmsg itch data/itch/rth-open.itch50 09:30:00 5

# the determinism proof: byte-identical replay
build/release/src/engine_replay --gen 500000 42 j.jrn
build/release/src/engine_replay j.jrn && build/release/src/engine_replay j.jrn