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
- 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.
- FLATNo position
- Entry order sentthen
- ENTRY_WORKINGEntry order sent, waiting for a fill
- Fill eventthen
- OPENPosition held; take-profit and stop orders placed
- Exit triggeredthen
- EXITINGClosing the position
- Interval endsthen
- 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.
| WebSocket | REST | |
|---|---|---|
| Role | Live source of truth | Administration and reconciliation |
| Carries | Market updates, trade and order events | Order 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 fills | Yes: fill events move the state machine | Only 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.

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.
01
Market data
WebSocket feed
- Market updates
- Trade and order events
02
Decision
Hot loop, no blocking calls
- Update state
- Build features
- Decide
03
API worker
Fed by a queue, off the critical path
- Place orders
- Cancel orders
- Retries and timeouts
04
Fill events
Back through the WebSocket
- Full and partial fills
- Order status changes
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.
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
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
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
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
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
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.
Related
Execution research
Trading 15-minute Polymarket markets until the data said stop
A vectorized backtester, walk-forward sweeps and a live order-book bot for 15-minute Polymarket markets, stopped when expected value after fees stayed near zero.
Latency and infrastructure
HFT on Polymarket: Rust, timestamps and the 98% hit rate that wasn't
Second-scale Polymarket research built recorder-first, with Rust on the hot path and LightGBM on top, and a 98% hit rate that fell to about 55% once latency was simulated.

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.