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 1 of 11

Stage 1

Market registry and instrument truth

Without a clean registry there is no shared notion of "the market". Every downstream stage reads from this one source.

Challenge we are solving

Markets resolve, retire, change tick sizes, change fees, or get listed under a new condition_id. If every consumer carries its own snapshot, they drift apart and trades land against stale assumptions.

What this stage does

Maintains the canonical instrument record for every tradable market — id, condition_id, token_ids, status, resolution rule, tick_size, min_size, fee_bps, negRisk flag, and a freshness timestamp.

Why this stage exists

Every signal, score, execution estimate, risk check, and OMS instruction has to dereference the same instrument. The registry is the dictionary they all read from.

Flow

Live APIPolymarket /markets
Normaliseschema · ticks · fees
Registrymarket_id · token_ids
status · tick · fee_bps
Freshnesslast_synced_ts

What the backend should expose

  • market_id, condition_id, token_ids (YES / NO)
  • status (open · paused · halted · resolving · resolved)
  • open_time, close_time, resolution_time
  • resolution_rule (URL or text), resolution_source
  • tick_size, min_size, fee_bps
  • negRisk flag, parent_event_id, category
  • last_synced_ts, freshness_ms

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

Price tick validation

\[price\_valid \iff (price \bmod tick\_size) = 0\]
SymbolMeaningUnits / range
\(price\)Quoted price of one share0 < price < 1 (USD per share)
\(tick_size\)Smallest price increment allowed0.01 typical; market-specific
worked example\[0.645 \bmod 0.01 = 0 \;\Rightarrow\; \text{valid};\quad 0.6453 \bmod 0.01 \neq 0 \;\Rightarrow\; \text{reject}\]

Any submitted price that fails this check is rejected client-side. Stage 8 also rejects again at the OMS boundary — same rule, two enforcement points.

2

Minimum size validation

\[size\_valid \iff size \ge min\_size\]
SymbolMeaningUnits / range
\(size\)Quantity of shares in the ordershares
\(min_size\)Per-market minimum order sizeshares (registry field)
worked example\[size = 2{,}000 \ge min\_size = 5 \;\Rightarrow\; \text{valid}\]
3

Per-trade fee estimate

\[fee = price \cdot size \cdot \frac{fee\_bps}{10{,}000}\]
SymbolMeaningUnits / range
\(price\)Fill price0 < price < 1
\(size\)Order quantityshares
\(fee_bps\)Exchange fee, basis points50 bps typical on Polymarket CLOB V2
worked example\[0.645 \cdot 2{,}000 \cdot \tfrac{50}{10{,}000} = \$6.45\]

Stages 6 and 7 both consume this number. The registry is the single source of fee_bps so the two stages never disagree.

4

Freshness window

\[freshness\_ms = now - last\_synced\_ts\]
SymbolMeaningUnits / range
\(now\)Current wall-clock timems epoch
\(last_synced_ts\)Timestamp of last registry syncms epoch
worked example\[now = 1{,}700{,}000{,}050{,}000,\; last\_synced = 1{,}700{,}000{,}047{,}500 \;\Rightarrow\; 2{,}500\,\text{ms}\]

If freshness_ms exceeds the registry SLO (default 10s) every downstream stage treats the market as stale and refuses to trade.

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.tsThe canonical TypeScript shape every stage imports. Treat this as the immutable schema.
  • packages/polytraders-bots/src/discoveryDiscovery bots that produce the registry feed: marketqualityranker, instrumentnormaliser, resolvability scoring.
  • packages/polytraders-runner/src/pipeline.jsThe pipeline harness that hydrates the registry once per cycle and passes it into every downstream stage.

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

  • DISC_*DISC — discovery, registry, instrument quality
  • INTEL_*INTEL — ingest, freshness, signals

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.