← Back

market-microstructure

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

A program that rebuilds an exchange's list of buy and sell orders (the “order book”) from NASDAQ's real data format (ITCH 5.0), keeps it up to date in billionths of a second, and flags four kinds of market manipulation as it goes.

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  (Windows 11, GCC 14.2, -O2, one thread)

[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 how the hardware reads memory, and how that shows up in speed
  • • 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

Memory reserved up front

Space for orders is set aside in advance, so adding or cancelling an order never has to ask the system for memory mid-trade (slow and unpredictable). If the space fills it doubles; in practice it doesn't.

Prices as whole numbers

Prices are stored as whole numbers in ten-thousandths of a dollar, exactly as they arrive. Decimal numbers can be off by a tiny amount and break equality checks; whole numbers can't.

Laid out for the processor

Each order is aligned to the processor's memory blocks. If this ever runs on several threads, two of them working on neighbouring orders won't slow each other down.

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.