Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board

← All stages · stage 3 of 11

Stage 3

Canonical state and the event log

Every stage agrees on the same facts because they all read from one append-only event log.

Challenge we are solving

If signals see one book and execution sees another, you cannot replay or explain a trade. Different stages must observe the same state at the same logical time.

What this stage does

Wraps each ingestion output in a typed canonical event with monotonic event_id, source_ref, correlation_id, source/recv/proc timestamps, and a content hash. Appends it to the event log.

Why this stage exists

The event log is what makes replay possible. It is the audit trail and the input to every backtest.

Flow

OrderBookSnapshotfrom stage 2
Normalisetz · units · fields
Hashbook_hash
Appendevent_log
Publishto signals / fair-value / OMS

What the backend should expose

  • event_id (monotonic), type, source_ref
  • correlation_id (groups all events for one decision)
  • source_ts, recv_ts, proc_ts
  • normalized fields (price, size, side, depth_levels)
  • quality_flags (clean · degraded · suspect)
  • book_hash, schema_version

Maths we expect here

Every formula below is implemented in packages/polytraders-bots/ or packages/polytraders-runner/. Treat the worked example as the unit-test sanity check you should be able to reproduce locally.

1

End-to-end lag

\[lag\_ms = proc\_ts - source\_ts\]
SymbolMeaningUnits / range
\(source_ts\)Time the venue produced the updatems epoch
\(proc_ts\)Time the canonical envelope was sealedms epoch
worked example\[proc=1{,}700{,}000{,}050{,}840,\; source=1{,}700{,}000{,}050{,}800 \;\Rightarrow\; lag=40\,\text{ms}\]

Stage 11 alerts when p99 lag breaches the SLO (default 250 ms).

2

Content hash for replay parity

\[book\_hash = \mathrm{SHA256}(canonical\_serialize(state))\]
SymbolMeaningUnits / range
\(state\)Normalised order-book state at this eventstruct
\(book_hash\)Hex digest carried on the canonical envelope32 bytes
worked example\[hash(\{seq{:}994, bid{:}0.62, ask{:}0.64,\ldots\}) = \mathtt{0x9a1d\ldots}\]

Stage 10 replays the event log and recomputes hashes — divergence means a non-deterministic dependency leaked in.

3

Monotonic event ordering

\[event\_id_t > event\_id_{t-1} \;\;\forall\,t\]
SymbolMeaningUnits / range
\(event_id\)64-bit monotonic counter per pipelineuint64
worked example\[event\_id\;\text{sequence}: 8801, 8802, 8803, \ldots\]

Out-of-order events are dropped — the canonical log is the audit trail; it must not lie about ordering.

How a developer codes this stage

Reference TypeScript implementation lives in packages/polytraders-* at the repository root. Stage owners maintain these files — read them before writing new code.

  • packages/polytraders-contracts/src/MarketSnapshot.tsCanonical types used everywhere. Stage 3 is responsible for filling every field, not just the cheap ones.
  • packages/polytraders-runner/src/context.jsBotContext — the way every bot reads canonical state. Bots never read raw feeds; they read context.
  • packages/polytraders-mock-clob-v2/src/envelopes.jsReference envelope writer; demonstrates correlation_id propagation through a trade.

See it in the platform mock

The platform mock is the source of truth for what each stage's UI exposes. Open these alongside the code references.

Reason codes emitted at this stage

  • INTEL_*INTEL — ingest, freshness, signals
  • GOV_*GOV — governance, accounting, replay, monitoring

Hover or tap any reason code on this page (or anywhere on the site) to see its canonical short description. Full registry: /standards/reason-codes.