A simple trading bot can be described in one sentence:
If a condition is true, place an order.
That approach becomes much more complicated when the market outcome depends on a TWAP (Time-Weighted Average Price) over a settlement window.
The bot now needs to understand not only the current market price, but also:
- where we are inside the settlement window
- which price samples have already been observed
- where the final average could finish
- whether the data is fresh
- whether the strategy is allowed to trade
- whether execution is safe
This article explains the engineering approach I use for a TWAP-aware Polymarket trading system.
1. The architecture
I think about the system as a pipeline:
Real-Time Data
↓
Data Validation
↓
TWAP Window Tracker
↓
Projected Settlement
↓
Signal Engine
↓
Risk Engine
↓
CLOB Execution
Each component has one job.
This separation is important because trading logic, data reliability, risk management, and execution have very different failure modes.
2. Start with the market-data layer
The first requirement is reliable real-time data.
A trading strategy is only as good as the information it receives.
The data layer should handle:
- incoming price updates
- timestamps
- subscriptions
- reconnects
- stale data
- missing samples
- duplicate events
- connection state
Conceptually:
┌─────────────────┐
│ Market / Oracle │
│ Feed │
└────────┬────────┘
↓
┌─────────────────┐
│ Event Processor │
└────────┬────────┘
↓
┌─────────────────┐
│ Data Validator │
└────────┬────────┘
↓
Valid Event
I don't want strategy code directly consuming an unvalidated feed.
3. TWAP requires state
With a normal price-based strategy, the latest price might be enough.
With TWAP, the system needs to remember what happened during the window.
A useful state model is:
WAITING
↓
ACTIVE
↓
COLLECTING
↓
SIGNAL_READY
↓
EXECUTING
↓
SETTLED
For each window, the system can maintain information such as:
windowStart
windowEnd
latestSample
sampleCount
projectedTWAP
dataFresh
strategyState
executionState
The exact implementation depends on the strategy, but the important concept is the same:
the trading engine needs memory.
4. Current price isn't the same as projected settlement
This is the key difference.
A naive strategy might use:
currentPrice
A TWAP-aware strategy is interested in:
projectedFinalTWAP
Conceptually:
Observed contribution
+
Expected remaining contribution
=
Projected settlement
As more observations arrive, the uncertainty around the final average can change.
So the signal engine should continuously update its view of the settlement window.
New price sample
↓
Update window
↓
Recalculate projection
↓
Evaluate signal
This makes the strategy time-aware rather than simply price-reactive.
5. Data freshness is a trading condition
One of the most important safeguards is stale-data detection.
Imagine:
Price feed
↓
Stops updating
↓
Bot still sees old price
↓
Strategy generates signal
↓
Order gets placed
That's a dangerous failure mode.
Instead:
Price feed
↓
Freshness check
↓
Fresh?
┌──┴──┐
Yes No
↓ ↓
Signal Stop
The system should be able to say:
I don't have reliable data, so I'm not trading.
This is a much better default than trying to recover a signal from stale information.
6. WebSocket reconnects aren't just networking
A common mistake is treating reconnect logic as a separate infrastructure problem.
For a trading bot, it isn't.
Suppose the WebSocket disconnects halfway through a settlement window.
After reconnecting, the system needs to know:
- Which market was being tracked?
- Which window was active?
- What was the previous state?
- Did any samples arrive during the interruption?
- Is the current data still valid?
- Did an order execute before the disconnect?
- Should trading resume immediately?
Therefore:
Disconnect
↓
Reconnect
↓
Restore / validate state
↓
Refresh data
↓
Resume only if safe
A reconnect should not automatically mean:
connected = true
start trading
7. Multiple windows need independent state
If you're tracking different market durations, don't put all timing logic into one global state.
For example:
5-minute window
↓
State A
15-minute window
↓
State B
4-hour window
↓
State C
Each window can have its own:
- timestamps
- samples
- projected settlement
- signal
- execution state
This makes concurrent market tracking much easier to reason about.
8. Separate strategy from risk
I prefer this separation:
Strategy
↓
"Is there an opportunity?"
↓
Risk Engine
↓
"Are we allowed to take it?"
↓
Execution Engine
↓
"How should we place the order?"
The strategy shouldn't be responsible for everything.
For example, the risk layer can check:
- maximum position size
- exposure
- duplicate orders
- market liquidity
- data freshness
- trading status
- other configured limits
That makes the strategy easier to test and modify.
9. Execution is another state machine
Placing an order isn't the end of the process.
The execution layer needs to handle states such as:
NO_ORDER
↓
ORDER_REQUESTED
↓
OPEN
↓
FILLED
But real systems also have failure paths:
ORDER_REQUESTED
↓
FAILED
↓
RETRY / ABORT
or:
OPEN
↓
PARTIALLY_FILLED
↓
FILLED
The exact behavior should be defined before connecting the strategy to real execution.
10. Logging every important event
When a trading system behaves unexpectedly, you need to reconstruct what happened.
Useful events include:
DATA_RECEIVED
WINDOW_STARTED
TWAP_UPDATED
DATA_STALE
SIGNAL_GENERATED
RISK_CHECK
ORDER_SUBMITTED
ORDER_FILLED
ORDER_FAILED
WEBSOCKET_DISCONNECTED
WEBSOCKET_RECONNECTED
WINDOW_SETTLED
A useful event log lets you answer:
Why did the bot make this trade?
without guessing.
That becomes especially valuable when comparing live behavior with backtest assumptions.
11. Backtesting isn't enough
A strategy can look good in a backtest and still fail during live execution.
The backtest may not fully represent:
- latency
- slippage
- liquidity
- stale data
- API failures
- reconnects
- partial fills
- rate limits
- timing differences
So I think about testing in layers:
Backtest
↓
Simulation
↓
Paper Trading
↓
Small Live Test
↓
Production
Each stage should answer different questions.
12. The complete system
Putting everything together:
┌──────────────────┐
│ Market / Oracle │
└────────┬─────────┘
↓
┌──────────────────┐
│ Data Validation │
└────────┬─────────┘
↓
┌──────────────────┐
│ TWAP Window │
│ State Manager │
└────────┬─────────┘
↓
┌──────────────────┐
│ Projected TWAP │
└────────┬─────────┘
↓
┌──────────────────┐
│ Signal Engine │
└────────┬─────────┘
↓
┌──────────────────┐
│ Risk Engine │
└────────┬─────────┘
↓
┌──────────────────┐
│ CLOB Execution │
└────────┬─────────┘
↓
┌──────────────────┐
│ Event Logging │
└──────────────────┘
The interesting part isn't any individual component.
It's how they behave together when something goes wrong.
13. The biggest lesson
A trading bot isn't just a strategy wrapped around an API.
A production-oriented trading system needs to understand:
data → state → strategy → risk → execution → recovery
That's particularly important for short-duration prediction markets, where a small timing or data-quality problem can change the entire decision.
For me, the goal isn't simply to make a script that can place an order.
It's to build a system that knows:
when to trade, how to trade, and when not to trade.
I'm continuing to experiment with Polymarket trading infrastructure, TWAP-based strategies, real-time market data, and automated execution.
Trading experiments can involve substantial risk. Backtests, simulations, and historical results do not guarantee future performance.
Top comments (0)