Polytraders Dev Guide
internal
Phase 1 · 9/109 wired

Start here

You are building a non-custodial platform for other people's trading bots. Users author the bots. We supply the guardrails that make running them safe, and an AI layer that helps users decide, place, and monitor. The 109 specs in this guide are the rails underneath that — not the product. Read what you are building first, then work the PR path below.

read first

The product model, in four sentences

Most of this guide reads like a manual for a 109-bot system we own end to end. That describes your first month of code; it does not describe the product. Somebody else's bot is the thing that trades. Our bots exist to constrain it, inform it, and explain it — which is why user code is sandboxed, why guardrails are a chain rather than a class, and why an LLM response can never become an order on its own.

  • The platform never holds user funds. Keys stay in the user's wallet. No omnibus account, no platform signer.
  • A user bot never reaches the venue directly. It produces a proposal. Exactly one component signs, and it is ours.
  • Guardrails cannot be configured away. Users tune thresholds inside bounds we set. They cannot remove or reorder the chain.
  • AI never holds Trade authority. Model output is advisory and enters the same RiskGate as any other intent.
operator UX mock

See where this work lands in the product

A high-fidelity re-skin of the eight live operator pages (Dashboard, Strategies, Risks, Bots, Decisions, Orders, Portfolios, Wallets) plus a new Pipeline page, showing exactly how the four-state promotion ladder, reason codes, envelope viewer, and V2-only rails surface to the operator. Each page is annotated with what changes vs. today.

first PR path

The first eight PRs, in order.

Each step is one reviewable PR. Do not skip ahead. Do not start a strategy PR before the rails it depends on are merged. PRs 1–5 are the shared rails and are largely shipped. PRs 6–8 are what turn those rails into the actual product — they are the ones currently missing, and they are the reason the earlier five exist.

  1. PR 1 — Contracts. Land MarketSnapshot, OrderBookSnapshot, OrderIntent, ReportEnvelope, and the ReasonCode registry as TypeScript types in packages/contracts. No bot code yet. See typed schemas →
  2. PR 2 — First risk bot. Implement risk.killswitch against the contracts from PR 1. KillSwitch is the smallest, most important bot. Use the Risk template. Add a fixtures/risk.killswitch/{normal,warning,hard,failure}.json pack. See KillSwitch spec →
  3. PR 3 — Fixture pack format. Land the four-file fixture-pack convention used by verify.js. Document it in Test pack. From this PR forward, every bot PR ships its fixtures in the same shape.
  4. PR 4 — Reporting envelope. Wire ReportEnvelope into the runner so every decide() emit lands a structured event. Free-text console.log is forbidden after this PR. See envelope spec →
  5. PR 5 — Demo wiring. Add the bot to wired_bots.json and to the synthetic browser demo. The synthetic feed is the only feed; production data does not exist yet. Open the demo →
  6. PR 6 — RiskGate, as the single write path. Collapse every route to the venue into one function that no other code can bypass. Move the guardrail chain behind it in a fixed order, make the hard guardrails non-disableable in config, and make the exposure reserve atomic over filled + pending + partial. Done when a grep for venue-client imports outside the gate returns nothing, and a test that tries to submit around the gate fails to compile. RiskGate standard →
  7. PR 7 — rules.yaml parser and the user sandbox. Land the Tier-1 schema, the bounds check that clamps user risk values to the platform floor (loudly, never silently), and signal-name resolution against the registry at save time. Then the isolate with its CPU, memory, and rate budgets, and the strike-to-quarantine path. Done when a user bot can be authored, backtested, shadowed, and paused without any of its code holding a key, a client, or a socket. User-bot standard →
  8. PR 8 — AI suggestion envelope. Land TradeSuggestion with mandatory evidence and registry-validated market ids, and route accepted suggestions through the same RiskGate as any bot proposal. Start with surface C (monitoring) — it is read-only, it is the highest-value surface for users, and it cannot lose money if it is wrong. Done when there is no code path from a model response to a signature. AI-assist standard →
what's next

Open work, before any new bot lands

  • Readiness taxonomy migration — replace legacy live/beta/planned labels with the five-state taxonomy (docs-complete / demo-wired / shadow-ready / runtime-live / production-live). Tracked as P0 #5.
  • Fixture packs for the 9 demo-wired botsverify.js currently warns; once fixtures land it will fail-on-missing in CI.
  • Spec/registry alignmentrisk.marketqualityguard aliases disc/marketqualityranker; risk.drawdownguard aliases risk/portfolioguard. Both need dedicated spec pages.
  • Atomic exposurerisk.portfolioguard currently checks open positions; needs to atomically include pending orders + partial fills before any second strategy ships.
  • Production-engine notes — idempotency, retry policy, monotonic clock, cancel-before-place, event sourcing. The runner today is a reference pipeline, not the production execution engine.

How to implement a bot

The same 7 steps for every bot, every class. Do not invent a different shape.

  1. Pick the class. Discovery, Strategy, Risk, Execution, Governance. The class determines authority, mode ladder, and which template you copy. By Class →
  2. Copy the template. The five class templates under /templates/<class>/ are the only approved starting point. Do not start from scratch. Templates →
  3. Paste the spec into your README. Open the bot's spec page. Paste it into the bot package's README so any spec change is reviewable in the same PR as the code change.
  4. Implement decide(ctx, intent). Everything else — interface, emit, explain, health — is already wired by the template. Your work is the decision function.
  5. Write the fixture pack. Four files: normal.json, warning.json, hard.json, failure.json. Copy expected outputs from the spec's wire-examples section — do not hand-write them.
  6. Run npm test. All four packages must stay green: contracts, synthdata, backtest, bots. The bots package runs verify.js: factory shape, decide() determinism, fixture-pack presence.
  7. Open the PR. The promotion-gate checklist is the PR template. Mark the readiness state honestly: a bot with a passing fixture pack is shadow-ready, not production-live.

The seven questions, in case you forgot one

  1. What do I build first? — Read the Plan. Phases 1–3 ship the shared rails; Phase 4 the risk gate; Phase 5 the execution rails; Phase 6 the first strategy. Skipping a phase means rebuilding it later under pressure.
  2. Which class does my bot belong to?Pick the class. Class determines authority, modes, and template.
  3. Which template do I copy? — The five class templates. The single approved starting point.
  4. Which schemas must I import? — All inputs and outputs come from the typed schemas. If the object isn't there, it isn't real yet.
  5. Which test fixtures must pass? — Every fixture pack ships {normal,warning,hard,failure}.json. Your decide() must reproduce the expected outputs deterministically.
  6. What counts as done?demo-wired requires 27/27 on the per-bot documentation checklist (section 29) and a passing fixture pack and a clean run of verify.js. That is documentation completeness plus reference implementation — it is not readiness to trade. Promotion to production-live requires shadow mode, runtime envelopes, and change control.
  7. How do I promote it? — Through the readiness ladder: docs-complete → demo-wired → shadow-ready → runtime-live → production-live. Never skip a state.

What you must never do

These are not style preferences. Each one, if broken, breaks the product's core promise, and each one is easy to break with a change that looks like an improvement.

NeverHow it usually gets brokenWhat breaks
Add a second path to the venue. A "quick" admin cancel endpoint, a migration script, a test helper that ships. The guardrail chain becomes decorative. RiskGate
Give user code a key, a venue client, or network access. A convenience helper injected into the sandbox context "just for reading prices". Non-custodial and the sandbox both. User bots
Let a guardrail fail open. A catch-all that returns ALLOW, merged as a resilience fix. The system trades hardest exactly when it understands least. Failing closed
Let a user loosen a hard cap. A config override that is read after the platform floor is applied. The floor. Order of application matters. RiskGate
Act on model output automatically. A monitoring loop that de-risks on its own "for the user's benefit". Advisory-only. That is Trade authority in a monitoring costume. AI assist
Widen a session-key method whitelist. Adding transfer to unblock a withdrawal feature. Custody. There is no bounded version of this. Signing model
Flatten a user's positions without authority. A revoke handler that "safely" closes everything out. Custody, in the one moment the user was asserting they did not grant it.

What does NOT belong on your machine

If you only read one other page

Read what you are building. It is the page that makes every other page in this guide mean the right thing. After that, bot-interface is the most important contract for the bots we supply, and RiskGate is the most important one for the platform.