This repository is a personal, non-commercial systems/performance engineering project. It implements a deterministic, event-driven simulator with a limit-order-book data structure so I can learn and benchmark:
- cache locality, memory allocation patterns (
std::pmr), and hot-path design - determinism/replay (seeded PRNG + replayable event streams)
- correctness invariants for a non-trivial state machine under load
It is NOT a trading system.
- No live trading: no order routing, no broker/exchange connectivity
- No live market feeds: no live exchange connectivity; no proprietary/paid data feeds
- No strategies/signals: does not implement alpha, execution logic, or investment advice
- No proprietary employer resources: built on personal time/equipment; not affiliated with any employer
- Synthetic data: default simulation uses generated events and synthetic prices (e.g., around a baseline mid such as $100)
If you’re here to evaluate engineering work: thank you. This repo is intended to be unambiguous about scope and usage so it’s easy to review and understand.
A high-performance, allocator-aware event simulator with per-symbol arenas, CPU pinning, and a fast per-symbol limit order book implementation. Designed for repeatable benchmarking, correctness, and maintainable hot-path code.
Current performance (Windows / MSVC / Ryzen 7 5800X, 6 pinned threads):
- Best: 42.4M events/sec
- Avg: 41.2M events/sec
(6 symbols, 2,000,000 events,sigma=0.001, 1 MiB arena per symbol,--no-log)
Results vary by CPU, compiler, and flags. Logging / I/O will reduce throughput drastically.
-
Limit Order Book (LOB)
- Strict price-time priority
- Internal tick-based prices (
int32_t tick) for determinism and speed - Flat hash price levels + pooled level reuse (avoids
std::map<double>pointer chasing) - Cancel index maintained for correctness (filled resting orders removed from index)
-
Simulation Engine
- Multi-threaded event generation and application (one symbol per thread by default)
- Deterministic ID + timestamp generation in benchmark mode (no realtime clock in hot loop)
-
Performance / Memory
- Per-symbol
std::pmr::monotonic_buffer_resourcearenas - CPU pinning support (best-effort on Windows/Linux)
- Bounded SPSC ring buffer implementation + unit tests (building block for future pipelining)
- Per-symbol
-
Persistence / Export (optional)
- LMDB-backed persistence + replay mode
- Optional Protobuf/gRPC export for local observability/visualization
- Off by default
- Intended for telemetry/inspection, not for production pipelines
- Recommended in single-thread mode unless otherwise stated in docs
include/msim/order_book.hpp— core order book API + structuresflat_hash.hpp— fixed-capacity flat hash with tombstone compactionspsc_ring.hpp— bounded SPSC ring buffersimulator.hpp— simulation engine interface
src/order_book.cpp— LOB implementationsimulator.cpp/main.cpp— harness + CLIstorage/— LMDB + optional export plumbing (not required for benchmark mode)
tests/spsc_ring_test.cpporder_book_test.cpp
scripts/bench.sh— repeatable benchmark runner (multi-config MSVC aware)
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 -DMSIM_WITH_GRPC=OFF -DMSIM_BUILD_TESTS=ON
cmake --build build --config Release -j
ctest --test-dir build -C Release --output-on-failurecmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DMSIM_WITH_GRPC=OFF -DMSIM_BUILD_TESTS=ON
cmake --build build -j
ctest --test-dir build --output-on-failureUse the included script (handles MSVC multi-config correctly and runs multiple reps):
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DMSIM_WITH_GRPC=OFF -DMSIM_BUILD_TESTS=ON
cmake --build build -j
ctest --test-dir build --output-on-failure
# then:
scripts/bench.shDefault config (override via env):
SYMBOLS=SYM1,SYM2,SYM3,SYM4,SYM5,SYM6EVENTS=2000000THREADS=6SIGMA=0.001ARENA_BYTES=1048576REPS=5
Example override:
THREADS=1 EVENTS=3000000 REPS=3 scripts/bench.shNote: Symbols are placeholders for per-instrument workloads; they are not intended to imply usage of any real security or dataset.
Best: 42,404,684 ev/s
Avg: 41,163,937 ev/s
(Windows 11, MSVC 19.44, Ryzen 7 5800X, 6 pinned threads, --no-log)
| Flag | Meaning | Default |
|---|---|---|
--events N |
total simulated iterations/events | 100000 |
--symbols CSV |
comma-separated symbol identifiers | SYM1,SYM2,SYM3 |
--threads N |
worker threads (typically = symbols) | auto |
--sigma X |
gaussian sigma (fraction of mid) | 0.001 |
--arena-bytes BYTES |
arena size per symbol | 1048576 |
--no-log |
disable persistence entirely | off |
--log PATH |
persist to LMDB | off |
--read PATH |
replay from LMDB | off |
--dump N |
when reading, print first N per symbol | off |
--print-arena |
show allocator telemetry | off |
--grpc HOST:PORT |
export events to collector | off |
Benchmarking tip: always use
--no-logunless you're explicitly measuring persistence/export.
- Tick-based prices avoid floating-key ordering issues and enable tight hashing.
- Flat hash maps + pooling reduce allocations and pointer chasing vs. trees.
- Tombstone compaction keeps open-addressing delete-heavy workloads stable.
- Benchmark harness avoids:
- contended global atomics in hot loops
- realtime clock calls per event
- string hashing/lookup per event
docs/architecture.md— core architecture + invariants (LOB / harness / arenas)docs/export.md— optional export path (e.g., gRPC/Protobuf), batching, and expected overheaddocs/persistence.md— LMDB log format + replay mode
This is a personal, non-commercial performance engineering project for educational purposes. It is not used for live trading, does not connect to brokers/exchanges, does not ingest proprietary or paid market data, and does not implement trading strategies, signals, or investment advice. Built on personal time/equipment; not affiliated with or endorsed by any employer.