A production execution platform in C++20 for running an already-finalized market-making strategy against crypto venues, in paper and live modes.
This repository is not a research platform. It contains no backtester, no parameter optimization, and no alpha research. The separate quant research platform owns those and hands this one a finalized strategy module plus its validated parameters.
Status: PHASE 10 OF 16 COMPLETE — NOT PRODUCTION READY. The pipeline now runs end to end: the Binance adapter synchronizes a real book, a strategy runs inside a runtime that contains it, the quote manager turns intent into desired order state, the risk engine decides what is permitted, the OMS owns the order lifecycle, a simulated venue executes against the live book and answers asynchronously, and the accounting layer turns the resulting fills into position, cost basis and PnL that risk enforces against. Nothing can reach a real venue with an order — no live execution adapter exists, and selecting one fails at startup rather than falling back. Phase 10 does not make the system production ready, does not prove any strategy profitable, and does not prove live execution safe. See Status.
Market Data → Order Book → Strategy → Quote Manager → Risk → OMS → Exchange
│
┌───────────┴───────────┐
▼ ▼
Paper Execution Live Execution
Four properties the architecture is built to guarantee:
- The strategy is replaceable. It is selected by name in YAML. Adding one
touches
strategies/and nothing else — not the engine, not risk, not the OMS, not the dashboard. - The exchange is an adapter. Binance is the reference implementation, not a dependency. No core module knows a Binance field name, endpoint, or error code.
- Paper and live share one engine. Identical strategy, market state, quote manager, risk, OMS, and portfolio. Only the execution adapter differs, which makes paper/live parity structural rather than aspirational.
- When uncertain, it stops. Stale data, a sequence gap, an invalid book, an unknown order, a failed reconciliation — every one has an explicit detector and an explicit response, and the response is never "carry on and hope".
Requires Linux, GCC 13+ or Clang 17+, CMake 3.24+.
System packages: Boost ≥ 1.74 (headers), OpenSSL 3, nlohmann/json, Google Benchmark.
sudo apt install libboost-dev libssl-dev nlohmann-json3-dev libbenchmark-devfmt, spdlog, yaml-cpp and GoogleTest are pinned git submodules, so every build resolves the same commits rather than whatever the distribution happens to ship.
git clone --recursive https://github.com/pranay123-stack/crypto-mm-engine.git
cd crypto-mm-engine
# already cloned without --recursive?
git submodule update --init --recursive
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build -j$(nproc)
ctest --test-dir build --output-on-failureSanitizers:
# Address + UB
cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug -DMMX_ASAN=ON && cmake --build build-asan -j
# Thread — requires Clang; GCC's TSan cannot model atomic_thread_fence
cmake -S . -B build-tsan -DCMAKE_BUILD_TYPE=Debug -DMMX_TSAN=ON \
-DCMAKE_CXX_COMPILER=clang++ && cmake --build build-tsan -jBenchmarks:
./build/mm_bench --benchmark_min_time=0.5s| Option | Default | Purpose |
|---|---|---|
MMX_WERROR |
ON | warnings are errors |
MMX_NATIVE_ARCH |
OFF | -march=native (off: portable binaries) |
MMX_LTO |
OFF | link-time optimization in Release |
MMX_ASAN |
OFF | address + UB sanitizers |
MMX_TSAN |
OFF | thread sanitizer (Clang only) |
MMX_BUILD_TESTS |
ON | test suites |
MMX_BUILD_BENCH |
ON | Google Benchmark targets |
Live trading requires six independent conditions, and no single file can supply them all:
mode: livein the configsafety.live_trading_enabled: true— a separate opt-in frommode- every risk limit set to a positive value (an unset limit is a refusal, never "unlimited")
risk.emergency_loss >= risk.max_daily_lossexchange.credentials_env_prefixnaming an environment variable pair that exists — credentials are never stored in config--livepassed explicitly on the command line
Copying config/live.yaml to another machine cannot cause it to trade.
tests/unit/test_config.cpp removes each gate in turn and asserts the engine
refuses to start.
config/paper.yaml is the default. Unknown keys are a hard error — a typo like
max_postion would otherwise parse cleanly, apply nothing, and leave the engine
running with an unbounded position limit.
Selecting a strategy:
strategy:
name: finalized_mm_v2
version: 2
params:
base_half_spread_bps: 4.0| Document | Covers |
|---|---|
| architecture.md | layers, module graph, boundaries, events |
| concurrency.md | threads, ownership, memory ordering |
| state-machines.md | order, session, book, and system states |
| exchange-interface.md | adapter contract, unknown-state semantics |
| binance-market-data.md | streams, normalization, reconnect, threading |
| order-book.md | book, invariants, synchronization algorithm |
| strategy-runtime.md | plug-in contract, lifecycle, replacement, failure behaviour |
| quote-manager.md | diff algorithm, ownership, generations, churn controls |
| risk-engine.md | exposure mathematics, fail-closed rules, kill switch |
| oms.md | order identity, lifecycle, reconciliation, recovery, invariants |
| execution.md | the execution interface, request and event models, asynchrony |
| paper-execution.md | fill model, queue approximation, latency, failure injection, replay |
| accounting.md | cost-basis model, PnL, fees, portfolio limits, recovery |
| benchmarks.md | measured numbers and what they imply |
Implemented and verified:
-
Phase 1 — Architecture & build. Module graph, strategy/exchange/paper-live boundaries, concurrency model, event model, state machines. CMake with three sanitizer configurations.
-
Phase 2 — Core infrastructure. Fixed-point money types, exact decimal parse/format, allocation-free identifiers, monotonic/wall clock separation,
Status/Result, bounded SPSC ring, seqlock, object pool, log-linear latency histogram, structured async logging, metrics with Prometheus exposition, and strict YAML configuration with live-mode gates.136 tests, clean under
-Werror, ASan+UBSan, and TSan. 17 benchmarks. -
Phase 3 — Exchange abstraction.
IExchangeMarketData/IExchangeExecutionand their sinks, normalized events, order requests with venue-rule validation, a capability model, and an explicit failure model whose central property is that a request that failed is never confused with a request the venue refused. A deterministic mock adapter implements both interfaces.279 tests, 24 benchmarks. The venue boundary is enforced mechanically by
tools/check_exchange_boundary.py, wired intoctest: no core file may name a venue or include an adapter header. -
Phase 4 — Binance market data and the local order book. TLS WebSocket with bounded jittered reconnect, REST snapshots, exact-decimal decoding, the full documented snapshot/delta synchronization procedure with gap detection and resync, a sorted-vector L2 book with continuously enforced invariants, and per-symbol staleness. Multi-symbol, with isolation asserted by test.
373 tests, 33 benchmarks. Verified against live public Binance data with
mm_md_probe: book synchronized, 0 decode errors, book uncrossed at the venue's 0.01 tick. Public data only — the adapter contains no order-entry code. -
Phase 5 — Strategy runtime.
IStrategy, an immutableStrategyContext,QuoteIntent, a lifecycle, a compile-time registry, and a runtime that gates on market-data health, times every call, catches exceptions, validates output against venue rules, and stops quoting on failure rather than standing on a stale quote. Strategy selection is a config change; the engine never names a concrete strategy.440 tests, 38 benchmarks. Includes one TEST/REFERENCE ONLY strategy that exists to validate the runtime — it is not a trading strategy and makes no profitability claim.
-
Phase 6 — Quote manager. Deterministic diffing of desired quote state against working orders into New/Cancel/Replace/Keep actions, with monotonic generation ordering, idempotency, explicit order ownership, conservative handling of unknown order state, partial-fill replenishment, and configurable churn controls that decide whether to rewrite an order but never what to write.
505 tests, 43 benchmarks. It contains no risk logic, no order management and no exchange code, enforced by the boundary checker.
-
Phase 7 — Risk engine. The safety boundary. Worst-case-per-side exposure arithmetic, position/notional/order/working/rate limits, price bands, a five-state machine with reduce-only and operator-only recovery from a kill, explicit and revalidated reduction, and overflow-checked fixed-point throughout. Every fail-closed path refuses new exposure while leaving cancellation available.
580 tests, 49 benchmarks. Contains no exchange code, no strategy logic and no order management, enforced by the boundary checker.
-
Phase 8 — Order management system. The single authoritative owner of order lifecycle state. Three never-conflated identities, a twelve-state machine with a legality table, trade-id fill idempotency with checked VWAP, exposure that counts anything possibly resting, an enforced cancel-then-new boundary, reconciliation that refuses incomplete snapshots and turns a missing order into
UNKNOWNrather than a cancellation, timeouts that admit ignorance instead of inventing an outcome, and restart recovery that distrusts every in-flight request.Risk cannot be bypassed by construction:
submit()accepts only arisk::ApprovedAction, whose validity flag onlyRiskEnginecan set. Twelve invariants are enforced and re-checked after every event in a generated-sequence sweep. Contains no venue code, no strategy logic and no market data, enforced by the boundary checker — including a check that paper and live never branch inside the OMS. -
Phase 9 — Execution layer and paper execution. A simulated venue behind the real execution interface: asynchronous by construction, holding its own order state that the engine cannot touch, matching against the same normalized book the strategy reads. Conservative displayed-liquidity fill model with a documented queue approximation, explicit post-only and marketable-limit semantics, five separately configurable latencies, deterministic failure injection by count rather than probability, cancel/fill race modelling, chunked open-order snapshots for reconciliation, and session record-and-replay.
Paper execution is an execution simulator for validating the live engine architecture — not a backtester. It loads no history and makes no claim about whether a strategy is any good. Selecting live execution fails at startup with a message naming the missing adapter; it never falls back.
745 tests, 88 benchmarks. Contains no network, no credentials, no venue wire format, and no strategy, risk or OMS logic, enforced by the boundary checker.
-
Phase 10 — Portfolio accounting and PnL. Weighted-average cost basis, realized and unrealized PnL, fee accumulation, per-symbol and portfolio aggregates, balances, and a snapshot/recovery contract. Every step is checked fixed-point arithmetic that refuses rather than wrapping, and every failure leaves state completely unchanged.
The limits Phase 7 configured but could not enforce —
max_portfolio_notionaland the loss limits — are enforced now, failing closed: an absent, stale or degraded ledger refuses new exposure rather than passing for want of a number. A faulted account never reads as flat.Accounting owns position, cost basis, PnL, fees and balances, and nothing else. It is not a second OMS: it consumes confirmed fills and has no API through which a position could be set directly. Property tests compare it against an independently implemented oracle rather than against itself.
812 tests, 97 benchmarks. Contains no venue code, no order lifecycle, no market data and no strategy logic, enforced by the boundary checker.
648 tests, 75 benchmarks.
Not yet implemented — every one of these is currently absent, not partial:
Phase 11 reconciliation & recovery · Phase 12 Binance live execution · Phase 13 operations dashboard · Phase 14 failure hardening · Phase 15 performance hardening · Phase 16 deployment.
No claim of production readiness is made, and none will be until the failure tests of Phase 14 pass.