Skip to content
All writing

Trading systems2026Published7 min

Execution playbook — part 2 of 2

A trading bot is infrastructure, not an edge

Notes from a live 15-minute bot and an HFT stack on Polymarket. The signal is one component; most of the work is state, fills, reconciliation and keeping blocking calls off the hot loop.

Systems
Live 15-minute bot and HFT execution stack on Polymarket
Stack
Python, Rust, WebSockets, REST
Covers
State machines, fills, reconciliation, risk rails
  • Polymarket
  • Trading bots
  • State machines
  • WebSockets
  • Reconciliation
  • Risk controls
  • Python
  • Rust
Control map of a trading bot: a WebSocket feed for market truth, a state machine for decisions, an API worker on the slow path, inventory and fill reconciliation, and safety bands and breakers
Context
I ran a live bot on Polymarket's 15-minute Up/Down markets, then built an HFT branch with a Rust hot path and a queue-backed API worker.
Lesson
The signal is one component. Most of the work is explicit state, fill handling, reconciliation, recovery and deciding which code is allowed to block.
Verdict
A bot makes a real edge repeatable and measurable. It cannot create one, and it exposes a weak one faster.

A bot is mostly coordination software

The popular version of bot building is "write a signal, call the API, make money." The real version is slower and more technical. Most of my time went into state reconciliation, execution discipline, failure recovery and deciding which code is allowed to block.

The useful lesson from Polymarket was not a strategy. It was what a bot has to become before it deserves to trade real money.

The popular version

  • Write a signal
  • Call the API and assume the order filled
  • Poll REST and treat the answer as live state
  • Let one loop place orders and wait on the network
  • Expect automation to create the edge

What a live bot needs

  • Explicit states that follow exchange events
  • Fill events as the only proof of execution
  • WebSockets for truth, REST for administration
  • Blocking API calls in a worker, off the hot loop
  • Recovery paths for partial fills, stale acknowledgements and state disagreements
  • Guardrails before ambition

State moves on fills, not on intent

The 15-minute bot ran on five explicit states. The rule that kept them honest: the state follows what the exchange reports, not the branch of code that wanted something to happen.

Transitions depend on fills and on time. The bot never marked a position open without a fill, and never marked it closed without a fill or a timeout followed by cleanup.

  1. FLATNo position
  2. Entry order sentthen
  3. ENTRY_WORKINGEntry order sent, waiting for a fill
  4. Fill eventthen
  5. OPENPosition held; take-profit and stop orders placed
  6. Exit triggeredthen
  7. EXITINGClosing the position
  8. Interval endsthen
  9. ROLLING_OVERMoving to the next 15-minute market

Other transitions

  • EXITINGFLAT— Exit filled, or timeout and cleanup
  • ENTRY_WORKINGFLAT— Order gone with nothing filled: cleanup
  • ROLLING_OVERFLAT— Next market ready

The five states of the 15-minute bot. Orders only express intent; a position opens or closes on a fill, a timeout or a cleanup.

WebSockets for truth, REST for administration

Each channel had one job. WebSockets carried market updates and fill events. REST was still needed to place and manage orders, but it was too slow to act as the trading heartbeat.

That gap shapes the architecture. Treat REST as a real-time source of truth and you misread both the market and your own position.

WebSocketREST
RoleLive source of truthAdministration and reconciliation
CarriesMarket updates, trade and order eventsOrder placement and management, plus get_order and get_open_orders checks
Timing in my notes~50 ms update cadence~500 ms per response
Trusted for fillsYes: fill events move the state machineOnly to reconcile: confirm or correct local state

Timings are the working figures from my research notes, not a benchmark.

What you hold is not what you have ordered

The bot kept two things apart: what it actually held, built only from fills, and what it had working in open orders. An order is intent. A fill is inventory.

Two rules follow. Never sell more than you hold. Never assume an order filled without a fill event.

A live bot also has to recover from partial fills, stale acknowledgements and moments when its local state and the exchange disagree. Skip that work and you end up debugging "why does the bot think it has 100 when the exchange says 0?" at 2 a.m.

Web dashboard of the live 15-minute bot showing status Running, uptime, balance, realized PnL, one open position, one open order, and an open-orders table with an active take-profit sell for 15 shares with nothing filled
The web dashboard I used to watch the live 15-minute bot. Positions and orders get separate panels because they are separate facts: this take-profit sell for 15 shares is ACTIVE with 0.00 filled, so the SOL position stays open.

The fast loop and the slow loop should not live together

A bot runs two kinds of work at very different speeds: reacting to the market, and waiting on the exchange's API. The HFT branch made the separation explicit. The hot loop listened, updated state, built features and decided. Blocking API work went into a queue served by a separate worker, so order placement happened outside the critical path.

  1. 01

    Market data

    WebSocket feed

    • Market updates
    • Trade and order events
  2. 02

    Decision

    Hot loop, no blocking calls

    • Update state
    • Build features
    • Decide
  3. 03

    API worker

    Fed by a queue, off the critical path

    • Place orders
    • Cancel orders
    • Retries and timeouts
  4. 04

    Fill events

    Back through the WebSocket

    • Full and partial fills
    • Order status changes
  5. 05

    Reconciliation

    Local state against the exchange

    • Inventory from fills
    • Open orders tracked apart
    • REST checks and ghost-order cleanup

The execution loop: market data, decision, queue-backed API worker, fill events, reconciliation. It closes on fills, not on the moment an order was sent.

Moving the blocking calls out is a small architectural idea with large operational consequences:

  • the decision loop stays responsive,
  • API jitter does not freeze the market loop,
  • failures are easier to isolate,
  • latency budgets are not spent on avoidable blocking calls.

The same constraint pushed the project toward Rust on the hot path and Python for orchestration, ETL and ML. That split was not aesthetic. It matched the runtime constraints.

Guardrails come before ambition

None of these rails looks glamorous in a screenshot. Each one mattered more than a clever, tweet-sized entry rule.

  1. 01

    Stop early enough to execute

    Trigger the exit when price comes within 0.10 of the stop, not 0.02. The stop order has to be in the book before price crosses the level, not after.

    Proactive stop · 0.10, not 0.02

  2. 02

    No re-entry in free fall

    After a stop, the bot waits until price has moved away from the last stop level before it can enter again.

    Post-stop protection

  3. 03

    Trade only inside a price band

    Refuse prices outside a safety band such as 0.10 to 0.90.

    Price band · 0.10–0.90

  4. 04

    Check the basics before going live

    Run pre-flight connectivity checks before live actions are enabled, and enforce minimum order sizes.

    Pre-flight checks · minimum size rules

  5. 05

    Trip a breaker when quality drops

    When latency or state quality degrades, stop acting instead of trading on a picture you no longer trust.

    Circuit-breaker logic

  6. 06

    Assume no exit is clean

    Cancel explicitly and reconcile against the exchange instead of assuming an exit went through.

    Explicit cancel, then reconcile

Good model metrics do not remove execution reality

The healthiest reset came from comparing research metrics with executable outcomes.

In the HFT stack, an adverse-selection classifier looked strong on standard ML metrics and still did not produce attractive maker-style PnL once latency, queue position and actual fills were taken into account. The details are in HFT on Polymarket.

The 15-minute work showed the same pattern. Once fees, slippage and live timing were included honestly, the market looked much closer to efficient than early backtests had suggested. The simulator side of that lesson is in How a real backtest works.

The live tests did not fail only on logic bugs. They failed on insufficient edge and on desync, and the desync is what forced the ghost-order cleanup, the proactive stop and the post-stop protection. That is why I treat bot building as systems engineering, not model building.

Bots amplify an edge; they do not create one

Bots are still valuable when they sit on top of a real edge or a disciplined research process. They enforce repeatability, capture detailed logs, expose hidden assumptions quickly and test operational hypotheses that manual trading never tests well.

What they do not do is create edge because they are automated. A serious bot is an execution system with data plumbing, state discipline and risk controls attached.

If the underlying edge is weak, the bot will reveal that faster. If the process is strong, the bot makes it measurable.

Seven checks before a bot trades real money

  • Positions open and close only on exchange events: a fill, a status change or a timeout.
  • Inventory comes from fills only. Open orders are tracked separately and never counted as a position.
  • WebSockets drive live state. REST places, manages and reconciles orders.
  • No blocking API call runs on the hot loop.
  • Orders the exchange has already closed are detected and cleaned up.
  • Price band, pre-flight checks and a circuit breaker are active before any size goes on.
  • The bot has passed a dry run before it gets live credentials.
Recorded 15-minute SOL window: the Binance ask (blue, left axis, 78.4 to 78.9) plotted against the Polymarket Up bid (orange, right axis, 0 to 0.9) over 900 seconds

Market systems2025–2026Stopped

Polymarket trading systems

Prediction-market research: a dataset builder, a 15-minute bot and a Rust + Python HFT recorder.

Get in touch

Working on something similar? I am happy to compare notes.