Simple, single-threaded limit order book in C++ with price-time priority. It keeps bids/asks in memory, supports limit and market orders, partial fills, cancel/replace by order id, IOC/GTC time-in-force, and emits trades when orders match.
submit returns a SubmitResult:
accepted: whether the order passed validation and entered matching.reject_reason: why an order was rejected (NONEfor accepted orders).trades: trades generated by that submission.
Validation currently rejects:
- duplicate order ids already resting in the book,
- non-positive price,
- non-positive quantity,
- market orders when the opposite book has no liquidity,
- replace requests for unknown order ids.
Prices are represented internally as integer ticks (PriceTicks) to avoid floating-point precision errors.
Use price_to_ticks(...) when constructing orders and ticks_to_price(...) only when formatting output.
src/core matching engine and book code.tests/assertions for matching, partial fills, cancel behavior, and safety validation.
cmake -S . -B build
cmake --build build
./build/matching_engine_appReplay mode:
./build/matching_engine_app replay tests/data/replay_basic.csv
./build/matching_engine_app replay tests/data/replay_basic.csv /tmp/trades.csvReplay mode parses CSV rows, sorts them deterministically by (ts_ns, seq, original_file_order),
replays them through the engine APIs, and prints a summary.
TWAP backtest mode (Piece 1):
./build/matching_engine_app backtest_twap tests/data/backtest_twap_basic.csv BUY 6 3VWAP backtest mode:
./build/matching_engine_app backtest_vwap tests/data/backtest_twap_basic.csv BUY 6 3Compare TWAP vs VWAP:
./build/matching_engine_app backtest_compare tests/data/backtest_twap_basic.csv BUY 6 3Batch experiment mode:
./build/matching_engine_app backtest_batch tests/data/backtest_batch_requests.csv
./build/matching_engine_app backtest_batch tests/data/backtest_batch_requests.csv /tmp/backtest_runs.csv /tmp/backtest_summary.csvBatch request CSV schema:
- Header:
dataset,side,qty,slices,strategy dataset: replay CSV path (for exampletests/data/backtest_vwap_profile.csv)side:BUYorSELLqty: positive integer parent quantityslices: positive integer number of schedule bucketsstrategy:TWAPorVWAP
Default batch outputs:
results/backtest_runs.csv: per-run metrics and status.results/backtest_summary.csv: aggregated strategy stats (mean/p50/p95) and pairedTWAP-VWAPdeltas.
Backtest modes replay the market CSV, inject market child orders on either a TWAP or VWAP schedule, and print execution metrics:
- fill quantity/rate,
- arrival benchmark and average fill price,
- implementation shortfall (bps),
- participation rate against replay market volume.
VWAP sizing is proportional to replayed market trade volume per time bucket. If replayed market volume
is zero, VWAP falls back to equal TWAP sizing. Time buckets with zero allocated VWAP quantity are shown
as SKIPPED child slices in the output.
Reproducible from:
./build/matching_engine_app backtest_batch tests/data/backtest_batch_requests.csvUsing the current tests/data/backtest_batch_requests.csv scenarios:
- Mean shortfall:
TWAP:17.6200bpsVWAP:17.1450bpsTWAP - VWAP:+0.4750bps (VWAP lower on average in this sample)
- Tail shortfall (
p95):TWAP:27.4750bpsVWAP:19.7145bps
- Fill rate:
TWAP:1.0000VWAP:1.0000
Example regime-dependent result from results/backtest_runs.csv:
backtest_vwap_profile.csv:TWAP=28.57bps vsVWAP=14.29bpsbacktest_twap_basic.csv:TWAP=6.67bps vsVWAP=20.00bps
This demonstrates the intended research workflow: compare execution policies by scenario, not just by global average.
- VWAP sizing currently uses replayed realized market volume (look-ahead), so it is for offline benchmarking and strategy comparison, not live execution deployment as-is.
Time-in-force behavior:
GTC: leftover quantity rests in the book.IOC: leftover quantity is canceled immediately.
Order type behavior:
LIMIT: matches only at limit price or better.MARKET: matches immediately against best opposite prices and never rests leftover quantity.
Replace behavior:
- Same price and quantity decrease (or unchanged quantity): keeps queue priority.
- Price change or quantity increase: loses queue priority (effectively cancel + new submit).
Market data API behavior:
top_of_book(): returns optional best bid/ask levels with aggregated quantity at each best price.depth(n_levels): returns topn_levelsaggregated levels for bids and asks.event_log()andevents_since(seq_num): provide sequenced incremental events (ADD,TRADE,CANCEL,REPLACE).
Replay CSV schema:
- Header must be exactly:
ts_ns,seq,action,order_id,side,type,price,qty,tif,new_price,new_qty,notes
action=NEW: requiresside,type,qty;pricerequired forLIMIT, blank forMARKET.action=CANCEL: requiresorder_id.action=REPLACE: requiresorder_id,new_price,new_qty.- Parsing errors include line numbers and stop replay.
ctest --test-dir build --output-on-failureMIT. See LICENSE.