← Back

market-microstructure

What happens inside an exchange between “buy” and “filled”?

A limit order book in C++20 that ingests NASDAQ's actual binary feed format (ITCH 5.0), keeps the book current in nanoseconds, and detects four kinds of market manipulation in the same process.

C++20CMakeITCH 5.0Pool allocatorFixed-point pricesctest
GitHub →

// why this exists

The problem I gave myself

Most of my work is measured in milliseconds — a slow API call, a lagging pipeline. I wanted to understand the world where a microsecond is slow. Where picking the wrong container or calling malloc at the wrong moment is the difference between competitive and irrelevant.

So I chose the hardest self-contained version of that world: rebuild NASDAQ's order book from the raw message feed, then detect spoofing the way an exchange's surveillance team would. No framework, no library doing the interesting part — just the feed, the book, and the detectors.

$ ./build/benchmark

[bench] add_order    : 672.7 ns/op  (1.5M ops/sec)

[bench] cancel_order : 29.1 ns/op   (34.3M ops/sec)

[bench] snapshot     : 1.0 ns/op

[test_order_book] all tests passed

[test_itch_parser] all tests passed

[test_detector] all tests passed

What this shows

  • • I can write C++ that holds up under a benchmark, not just compiles
  • • I know what a cache line is and why alignas(64) matters
  • • I can parse a binary wire protocol byte by byte — and test it byte by byte
  • • I understand how exchange matching actually works, not just the theory

Honest engineering notes

  • • First benchmark claimed 45ns/add. Real measured number: 673ns. The map rehashing was invisible until I measured properly — the README tells that story.
  • • The parser passed every “looks right” check and was off by 2 bytes on every field. Byte-level tests caught it.

// the surveillance layer

Four manipulation patterns, caught in-process

Spoofing

A huge bid appears to fake demand, never trades, and vanishes in under 500ms.

Track each order's size against book depth. Flag oversized cancels inside the time window.

Layering

Four or more orders stacked at adjacent prices on one side, creating fake depth.

Rolling window over open orders per symbol; flag clusters within a few ticks.

Momentum ignition

A burst of aggressive one-sided fills meant to start a price move.

Count same-direction fills in a 2-second sliding window.

Quote stuffing

Flooding the feed with 10,000+ messages a second to slow competitors down.

Message-rate counter per symbol per second. Simple and effective.

// design decisions

The parts I'd defend in a review

Pool allocator

Orders live in a pre-allocated flat array with a free list. After warmup, add and cancel never call malloc. The pool doubles if it fills — in practice it doesn't.

Fixed-point prices

All prices are int64 in 1/10000-dollar units, exactly as they arrive on the wire. No floating-point equality bugs in price level lookups.

Cache-line alignment

Order is alignas(64). If this ever goes multi-threaded, two threads on adjacent orders won't invalidate each other's cache lines.

The tests are the spec

14 tests including byte-level parser cases. The off-by-two bug I mentioned above is now a permanent regression test.