A production-grade market-data system for Polymarket order books, written in Rust: lock-free single-writer ingestion over zero-copy WebSocket deserialization, an embedded CRC-framed write-ahead log with torn-tail crash recovery and flock-based writer failover, split Parquet + ClickHouse storage, deterministic point-in-time book reconstruction, and a read-only quant workstation (HTTP/WS/gRPC + React UI) on top.
Live demo — the workstation UI
in the browser, no install. Or fully offline with real captured market data:
just demo.
Measured hot-path medians (Apple M3, full context and 49-benchmark table in docs/PERFORMANCE.md):
| Operation | Median | Rate |
|---|---|---|
| Book delta apply | 9 ns | 108 M/s |
| WAL codec encode | 87 ns | 11.5 M/s |
| WAL append + flush | 509 ns/record | 2.0 M/s |
| Dispatcher normalize + shadow-book cross-check | 484 ns/entry | 2.1 M/s |
WS price_change deserialize (zero-copy) |
504 ns | 2.0 M/s |
| Read-model snapshot (50 levels) | 154 ns | 6.5 M/s |
- Live demo or
just demo— see it run (10 seconds / one command). - crates/pb-wal/README.md — the embedded WAL:
CRC-over-length framing, torn-tail truncation on recovery, single-writer
flockthat doubles as failover, independent tailing consumers. - TESTING.md — the failure-mode → defense matrix: 762 tests, 35 property suites, 6 fuzz targets, miri, offline alert-rule incident tests, and an honest list of what is not yet covered.
- docs/PERFORMANCE.md — measured, machine-stamped numbers for every pipeline stage.
- research/orderbook_analysis.ipynb — microstructure analytics (order-flow imbalance, microprice, realized spread) computed straight off the committed Parquet capture.
- docs/adr/ — eleven ADRs from fixed-point arithmetic to the read-only workstation boundary, each with alternatives and tradeoffs.
An active personal project, open to outside contributions. The ingestion, durability, and replay paths are working and heavily tested; APIs and storage layout may still evolve. Start with CONTRIBUTING.md.
just demo # or: cargo run --release -- demoReplays a committed capture of real Polymarket BTC-5-minute market data as a
simulated live feed behind the full API — the book ticks at its original
cadence, and replay/integrity/execution answer from the capture. No network,
no venue dependency, deterministic. The startup banner prints copy-paste
curl examples with known-good timestamps.
The same capture is a research dataset: plain hour-partitioned Parquet with a
documented schema. research/orderbook_analysis.ipynb
is an executed notebook (renders on GitHub) computing microstructure analytics
over it — mid/spread, microprice, order-flow imbalance vs price moves, trade
flow, and binary-outcome pinning — with polars + matplotlib only; see
research/README.md for a one-line runner. For the API
surface, examples/api-cookbook.md has one
captured request/response example per route, all runnable offline against
just demo.
docker compose --profile minimal up --build
# open http://localhost:3000 — live feed + API + workstation UI in one containerThe full profile runs the production process separation (ingest + serve over
a shared WAL volume, plus ClickHouse), and observability adds
Prometheus/Alertmanager/Grafana with the committed dashboard. See
docs/operations.md for all three topologies.
- Rust 1.94+
- Docker for the full integration suite
justis optionalduckdbis optional for Parquet inspection- ClickHouse is optional unless you want warm-storage testing
cargo buildcargo test --workspace --exclude pb-integration-testsjust ciThis runs formatting, Clippy, and the full test suite. The integration package
includes ClickHouse round-trip coverage through testcontainers, so Docker must
be available.
Integration tests live in pb-integration-tests and require Docker (for
ClickHouse via testcontainers). They are excluded from the default test run:
# Run integration tests locally (requires Docker)
cargo test -p pb-integration-tests -- --test-threads=1cargo run -- discover --filter btc --limit 5Expected output shape:
--- Live BTC 5-minute market ---
Event: <event title>
Market: <market question>
Token IDs: <yes_token_id>,<no_token_id>
Active: true
The exact market title and token IDs change over time.
just discover
# or
cargo run -- discover --filter btc --limit 100just auto-ingest
# or
cargo run -- auto-ingestContinuously rotates to the live BTC 5-minute market and ingests data.
just ingest <TOKEN_ID>
# or
cargo run -- ingest --tokens <TOKEN_ID> --parquet --metricsjust replay <TOKEN_ID> <TIMESTAMP_US>
# or
cargo run -- replay --token <TOKEN_ID> --at <TIMESTAMP_US> --source parquet --mode recv_timeReplay modes:
recv_timereproduces local observation orderexchange_timereorders by venue timestamp, then receive time, then sequence
Add --validate to compare replay output against the next checkpoint.
cargo run -- serve-api --tokens <TOKEN_ID>
# or
cargo run -- serve-api --auto-rotateThis starts the read-only API layer for the quant workstation backend. The first release serves live feed status, active assets, live book snapshots, and Parquet-backed replay reconstruction.
cd web
nvm use
npm ci
npm run devBy default the Vite dev server binds 127.0.0.1:4173 and proxies /api
requests to http://127.0.0.1:3000, so you can run it alongside
cargo run -- serve-api --auto-rotate. Set VITE_DEV_HOST=0.0.0.0 only for an
intentional LAN-exposed dev session. The shipped SPA currently includes Live Feed, Replay Lab, Integrity,
and Execution Timeline. Append
?source=demo to the URL or use the in-app toggle to review seeded sample data
without a running backend. The SPA uses a newer Node runtime than the Rust-only
workspace commands; see web/README.md for the exact supported
version and validation flow.
cargo run -- execution-replay --start <START_US> --end <END_US> --source parquetjust backfill <TOKEN_ID>
# or
cargo run -- backfill --tokens <TOKEN_ID> --interval-secs 60# Stop ingest and every other Parquet writer first. The command verifies the
# exclusive WAL lease; standalone backfill/append writers are an operator check.
cargo run -- reconcileRecovery fails closed on WAL corruption and partial boundary hours. For every
fully covered receive-time-partitioned UTC hour (book_events, trade_events,
and ingest_events) it writes and verifies a new Parquet object, publishes an
authoritative manifest, and only then cleans superseded objects. Checkpoint,
validation, and execution partitions are left untouched because their partition
timestamps do not provide the same WAL coverage proof. Local files and S3 use
the same object-store reader and manifest semantics.
just parquet-stats
just parquet-peek
just parquet-schemaThe full failure-mode → defense matrix — which layer catches which class of bug, with real counts and run commands — lives in TESTING.md.
Measured results — hardware- and commit-stamped medians for every hot-path
stage (wire deserialize, dispatcher normalize, WAL append, book ops, read
model) — live in docs/PERFORMANCE.md, regenerated with
just bench-report; the headline medians are in the table at the top of this
README.
cargo bench # all benchmarks
cargo bench -p pb-types # fixed-point ops + wire deserialization throughput
cargo bench -p pb-book # book operations, depth iteration, mixed workloads
just bench-report # full suite + regenerate docs/PERFORMANCE.mdThe pb-types and pb-book crates include proptest suites that verify
invariants across randomized inputs:
- fixed-point roundtrip, ordering, and serde consistency
- bid/ask ordering preserved after arbitrary snapshots and deltas
- spread never negative for non-crossed books
- mid price and weighted mid price bounded by best bid/ask
- sequence monotonicity and gap detection soundness
- snapshot idempotency
- crossed-book detection via
check_integrity() - total size consistency across all levels
Fuzz targets under fuzz/ cover deserialization and book mutation:
cargo +nightly fuzz run fuzz_ws_deser # wire protocol parsing
cargo +nightly fuzz run fuzz_fixed_price # FixedPrice/FixedSize parsing
cargo +nightly fuzz run fuzz_book_delta # book delta application invariants
cargo +nightly fuzz run fuzz_query_guard # SQL query guard sanitizer + LIMIT normalizationKey design choices documented in docs/latency.md and docs/adr/:
- Fixed-point arithmetic eliminates FPU stalls and NaN guards (ADR-0001)
- BTreeMap book: best bid/ask in ~3 ns measured, benchmarked against a dense-array alternative (ADR-0002)
- Channel-based message passing avoids locks on the hot path (ADR-0003)
- Zero-copy wire deserialization reduces allocations (ADR-0004)
- mimalloc global allocator for lower p99 latency (ADR-0005)
- FxHashMap in Dispatcher for faster internal lookups (ADR-0006)
The project demonstrates a specific style of trading-infrastructure design:
- fixed-point numeric types instead of heap-heavy decimal math
- single-threaded book mutation on the hot path
- bounded channels and backpressure between components
- typed, split datasets instead of one overloaded event stream
- replay and validation as first-class storage consumers
Polymarket WS -> pb-feed -> pb-wal -> pb-store -> Parquet / ClickHouse
| | ^
v v |
pb-api <---- pb-service <-- pb-replay --+
|
+-> workstation clients
|
+-> pb-metrics
For detailed data flow diagrams, crate dependency graphs, and runtime topology, see docs/architecture.md.
Workspace crates:
| Crate | Responsibility |
|---|---|
pb-api |
Read-only HTTP API and live read model for workstation clients |
pb-types |
Fixed-point types, wire formats, and persisted records |
pb-book |
In-memory L2 order book engine with analytics (weighted mid, top-N, integrity checks) |
pb-feed |
WebSocket ingest, REST discovery, dispatcher, rate limiting |
pb-store |
Parquet and ClickHouse sinks |
pb-replay |
Historical replay, checkpoint reconstruction, validation, backfill |
pb-wal |
Embedded write-ahead log with append-only segments and CRC32C framing |
pb-service |
Transport-neutral domain service layer with configurable backends |
pb-grpc |
gRPC read surface using tonic (optional, delegates to pb-service) |
pb-metrics |
Prometheus metrics endpoint |
pb-bin |
CLI entrypoint |
Runtime config is layered:
config/default.toml- environment variables prefixed with
PB__ - CLI flags
Example:
PB__LOGGING__LEVEL=debug cargo run -- auto-ingestOperational details, deployment notes, infrastructure setup, and data layout live in docs/operations.md.
Current workstation API routes and runtime notes live in:
Near-term work that would make strong public contributions:
- broader replay validation and determinism coverage
- workstation integrity, latency, execution, and query expansion beyond the current Live Feed + Replay Lab SPA slice
- more explicit schema/versioning guarantees for persisted datasets
- clearer operational docs for ClickHouse and cloud object storage
- examples for strategy hooks and execution-event workflows
This repository uses OpenSpec for larger
feature work. Archived proposals, designs, specs, and tasks live under
openspec/changes/archive/.
This project is built AI-assisted under human design direction and review. Correctness does not rest on review alone: every change passes hard verification gates — the full test suite (fuzzing, property tests, miri, golden byte fixtures, offline alert-rule incident simulations; see TESTING.md), adversarial review passes hunting for bugs rather than confirming intent, and live end-to-end verification against the real venue before merge. Design decisions and their rejected alternatives are recorded in docs/adr/ and the archived OpenSpec changes rather than in tooling history.
For contribution standards and PR expectations, see:
