Skip to content

Repository files navigation

Matching Engine

A standalone, deterministic trading matching engine in Rust.

Headline invariant

The same ordered command sequence produces byte-identical state and output — on any platform, any future build of the same engine-core version. Everything else (performance, features) is subordinate to that.

Workspace

Crate Type Responsibility
engine-core lib All matching logic: book, order types, matching loop, and the canonical state snapshot. No I/O, async, networking, SQL, or clock. The reusable jewel.
engine-wire lib The wire protocol: length-prefixed bincode frames, order-entry + market-data vocab. Versioned independently of the journal.
engine-server bin Wraps the core: append-only journal, replay/recovery + snapshot-accelerated recovery, the sequencer + clock, the durability watermark, the durable-gated market-data channel, the SQL projection, and the networked service.
engine-client lib The Rust SDK: order entry + market data, the provisional/committed contract, and an auto-reconnecting transport with exactly-once resubmit.

Build & test

cargo build
cargo test --workspace
cargo clippy --workspace --all-targets   # determinism lints (float ban)

Run the server

cargo run -p engine-server --example serve

Boots a real server (sequencer thread + order-entry/market-data over loopback TCP) and drives it end-to-end: register, rest, subscribe, cross the book, see the pushed update, clean shutdown.

Performance

In-memory, single-threaded, integer-only — and benchmarked, not asserted. Tiers are reported separately, never blended. Numbers are indicative (unpinned Ryzen 9 6900HX dev laptop; official figures want a pinned runner).

Tier 1 — engine-core hot path, zero I/O

cargo bench -p engine-core. Throughput is batch-timed; latency is sampled per-op (so it includes timer overhead — read it as an upper bound).

command throughput p50 p99 p99.9 (ns)
add (rest on a ladder) ~1.8 M orders/sec ~280 ns ~880 ns ~1.4 µs
cancel (by id) ~5.1 M cancels/sec ~140 ns ~920 ns ~1.7 µs
marketable, 1 fill ~2.8 M matches/sec ~310 ns ~1.15 µs ~1.9 µs
deep sweep (64 fills) ~14 M matches/sec
add, expiry sweep active ~12 K orders/sec ~82 µs ~158 µs ~203 µs

The first four rows are the fast path (cost dominated by the canonical BTreeMap price ladders). The last row was a ~150× cliff — a single GTD order armed an O(book) expiry sweep on every command; a lazy O(due) index fixed it, byte-identically.

Tier 2 — end-to-end, networked

cargo bench -p engine-server. The two latencies are reported separately: the provisional ack (no fsync except PerEvent) and the commit watermark (per durability knob). File journal on tmpfs — fsync cost is storage-dependent.

latency p50 p99
provisional-ack (open-loop, coordinated-omission-resistant) ~54 µs ~139 µs
provisional-ack (closed-loop round-trip) ~43 µs ~90 µs
commit — PerEvent / OsFlushOnly (window 0) ~20 ns ~40 ns
commit — GroupCommit (2 ms tick) ~2.0 ms ~2.1 ms

GroupCommit keeps the ack fast but pays ~one tick at commit; PerEvent folds durability into the ack. (This bench caught a real bug: a missing server-side TCP_NODELAY → ~40 ms Nagle stalls, now fixed.)

Tier 3 — sustained soak

cargo bench --bench stress (both crates) drives a mixed workload for millions of commands. The honest finding: sustained core throughput degrades (~1.07 M → 475 K cmds/sec over 10 M commands) and RSS climbs to ~1 GB — not the matching loop (per-command stays sub-µs) but the unbounded dedup table (see Limits). The networked service saturates near ~110 K acks/sec (single sequencer + fsync), with sub-ms→low-ms p99 below that.

Determinism gate ("from day one")

engine-core/tests/golden_replay.rs runs a fixed corpus, canonically encodes the (Seq, SubSeq) event stream, and pins it by hash — asserting two runs are byte-identical and match the pin. Integer-only math makes that pin a true cross-platform fingerprint, exercised on x86_64 + aarch64 in CI.

Status

Feature-complete, built and verified phase by phase (each phase = a feature commit + a multi-agent verification commit):

  • deterministic core + matching, with a golden replay-hash gate + a differential suite vs an independent reference matcher;
  • durable journal + replay/recovery, the sequencer + durability watermark, the wire protocol, the durable-gated market-data channel, and a runnable server (order entry
    • market-data push over UDS/TCP);
  • SQL projection (journal → SQLite), snapshots + accelerated recovery, and the full SDK (both channels, the provisional/committed contract, auto-reconnecting transport with client_order_id dedup);
  • Tier-1/2/3 + recovery benchmarks; CI runs the suite + clippy on x86_64 and the determinism gates on aarch64 (via cross/QEMU).

Remaining is polish, not features: the cascade-depth breaker for hostile/seedable books (v1-deferred) and O(1) intrusive-list order cancel (benchmark-gated).

Deployment: assumptions & limits

Built for a trusted, single-economy session (a 1–15-player floor); pre-1.0, the API may change. None of these are determinism bugs — they are v1's documented edges.

  • Trusted single peer. Framing is DoS-bounded, but the server isn't hardened against a hostile multi-tenant client. UDS uses file-permission auth; TCP needs the per-session secret.
  • Bounded sessions. The dedup table and journal grow with distinct client_order_ids / command count — fine for a game session, but a long-lived, high-throughput deployment needs windowing/compaction (not yet built). Recovery reads the whole journal into memory; snapshots skip the replay, not the bytes.
  • Single process, recover-on-restart. No sharding or failover; availability is restart + replay/snapshot recovery.
  • No cascade-depth breaker yet. A non-adversarial book can't cascade unbounded, so v1 ships uncapped; a seedable public book wants the (designed) max_cascade_depth breaker first.
  • Determinism scope. Guaranteed within one engine-core version (mismatches refused). Floats are compiler-forbidden; the cross-platform hash runs on x86_64 + aarch64 in CI.

About

Nundinae is a matching engine that I created for my game, Echoes of Ether.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages