Why a Polymarket Trading Bot Needs State Reconciliation
A trading bot can keep running while its internal state is already wrong. That is one of the most dangerous failure modes in automated trading. The obvious problems are easy to notice: the process crashes the WebSock
A trading bot can keep running while its internal state is already wrong.
That is one of the most dangerous failure modes in automated trading.
The obvious problems are easy to notice:
- the process crashes
- the WebSocket disconnects
- an API request fails
- an order is rejected
The harder problems are silent:
- a partial fill was missed
- an order was cancelled remotely but remains open locally
- a reconnect caused an event gap
- local position state no longer matches exchange state
- the bot continues trading using stale exposure data
At that point, the bot may still look healthy.
It just isn't trading from reality anymore.
For a serious Polymarket trading system, state reconciliation needs to be a first-class component.
The basic problem
Imagine the bot submits:
BUY 100 contracts @ 0.57
Its local state becomes:
order_id = 123
status = OPEN
size = 100
filled = 0
Then 40 contracts fill.
The correct state is now:
status = PARTIALLY_FILLED
filled = 40
remaining = 60
But suppose the WebSocket disconnects at exactly the wrong time.
Your process reconnects.
The event containing that fill is gone.
Your local state still says:
filled = 0
remaining = 100
Now the strategy can make completely different decisions from the ones it should make.
It might:
- place another order
- calculate incorrect exposure
- report incorrect PnL
- apply the wrong risk limit
- cancel an order that isn't actually in the state it expects
Nothing necessarily crashes.
That is what makes stale state dangerous.
WebSocket events are not enough
Real-time events are essential for a trading system.
They let the bot react quickly to order and trade changes without constantly polling.
But there is an important architectural distinction:
Events tell you what happened. Reconciliation tells you what is true now.
A production system needs both.
I think about the architecture like this:
REAL-TIME PATH
Polymarket CLOB
|
WebSocket
|
Event Processor
|
Local State
orders / fills /
positions / risk
RECONCILIATION PATH
Remote API State
|
Reconciliation
|
Local State
|
State Repair
The real-time path optimizes for speed.
The reconciliation path optimizes for correctness.
1. Turn events into explicit state transitions
One mistake is letting every incoming event directly mutate arbitrary application state.
Instead, model the trading lifecycle explicitly.
For example:
ORDER_PLACED
β
OPEN
β
PARTIALLY_FILLED
β
FILLED
Cancellation has its own lifecycle:
OPEN
β
CANCEL_REQUESTED
β
CANCELLED
And failures should also be explicit.
This gives you a state machine that can be tested and replayed.
Instead of asking:
Why does the bot think it owns 400 contracts?
you can ask:
Which state transition caused the position to become 400?
That makes failures much easier to debug.
2. Make event processing idempotent
Real-time trading systems eventually encounter:
- duplicate events
- retries
- reconnects
- replayed messages
- process restarts
Processing the same event twice should not create two fills.
A simplified example:
def process_event(event):
if already_processed(event.id):
return
apply_transition(event)
mark_processed(event.id)
The exact implementation depends on the system, but the principle is important:
The same event should produce the same state exactly once.
This becomes especially important when rebuilding state after a restart.
3. Reconcile after reconnects
A reconnect shouldn't simply mean:
connect
β subscribe
β resume trading
A safer flow is:
disconnect
β
reconnect
β
load persisted state
β
query current remote state
β
compare local vs remote
β
repair differences
β
verify risk
β
resume trading
For example:
LOCAL REMOTE
Order 123: OPEN Order 123: OPEN
Filled: 0 Filled: 40
Order 456: FILLED Order 456: FILLED
Position: +100 Position: +100
The reconciliation layer identifies:
Order 123:
local filled = 0
remote filled = 40
β local state is stale
β repair local state
Only after that should the strategy continue.
4. Separate orders, fills, and positions
These are related, but they are not the same thing.
A useful hierarchy is:
Orders
β
Fills
β
Positions
β
Exposure
β
Risk
Consider:
Order A: BUY 100
Order B: BUY 50
Then:
Order A β filled 60
Order B β filled 50
The position isn't 150.
It is based on the actual executed fills.
This distinction becomes critical once you have partial fills, multiple orders, cancellations, and concurrent strategy actions.
A robust trading system should never derive actual exposure from intended order sizes alone.
5. Order lifecycle is not a single state
Another common mistake is treating:
order submitted
as equivalent to:
order executed
They are different stages.
A more realistic lifecycle is:
INTENDED
β
SUBMITTED
β
ACCEPTED
β
MATCHED
β
FILLED
With alternative paths:
SUBMITTED β REJECTED
SUBMITTED β CANCELLED
MATCHED β RETRYING
The strategy layer needs to know which state it is actually in.
This becomes particularly important in asynchronous execution systems, where an order can be accepted before the full execution lifecycle is known.
6. Persist state
If the process restarts, memory disappears.
A production system should persist enough state to reconstruct what happened.
At minimum, that usually means tracking things like:
order ID
market
side
requested size
filled size
remaining size
status
timestamps
event IDs
trade IDs
For positions:
market
position size
average entry
realized PnL
unrealized PnL
last update
The goal isn't to persist every internal object.
The goal is to preserve enough information that the system can:
- restart
- inspect the remote state
- reconcile
- continue safely
7. Detect gaps instead of assuming everything arrived
A WebSocket connection can be alive while your application is still missing information.
That means your system should have some notion of continuity.
For example:
event 1001
event 1002
event 1004
Where is:
event 1003?
A trading system shouldn't silently assume that the missing event doesn't matter.
This is where sequence tracking, timestamps, persisted event IDs, or explicit resynchronization logic can become useful.
The exact mechanism depends on the stream and API.
The architectural principle is the important part:
Don't confuse βsocket connectedβ with βstate synchronized.β
8. Reconciliation should be safe
Reconciliation itself can create dangerous behavior if implemented badly.
Suppose local state says:
position = +100
but remote state says:
position = +40
The solution isn't necessarily:
position = 40
You first need to understand why the states differ.
Was there:
- a missed fill?
- a delayed event?
- a duplicate event?
- an unexpected trade?
- a stale API response?
- a bug in local processing?
A useful reconciler should produce a discrepancy such as:
Position mismatch
Local:
+100
Remote:
+40
Difference:
-60
Reason:
unmatched fill / state gap
Action:
repair + log + alert
That makes reconciliation observable and debuggable.
9. Add a trading safety gate
One of the most useful patterns is to make reconciliation part of trading permission.
Conceptually:
if not state.is_consistent():
trading_enabled = False
Then:
NORMAL
β
STATE MISMATCH
β
TRADING PAUSED
β
RECONCILIATION
β
STATE VERIFIED
β
TRADING RESUMED
This is much safer than allowing the strategy to keep trading while its view of exposure is uncertain.
You don't always need to shut down the entire system.
Depending on the risk model, you could:
- pause new entries
- allow exits only
- reduce position size
- disable a particular market
- require manual approval
The important part is that strategy execution becomes conditional on trusted state.
10. Recovery should be designed before failure
A common development pattern is:
βWe'll deal with reconnects later.β
That usually becomes painful once the system has real positions.
A better architecture defines recovery from the start:
process starts
β
load persisted state
β
connect
β
synchronize
β
reconcile
β
validate risk
β
start strategy
And after a failure:
failure
β
reconnect
β
detect possible gap
β
reconcile
β
repair
β
resume
Recovery is not a special case.
It is part of normal trading-system behavior.
A practical architecture
Putting the pieces together:
POLYMARKET CLOB
|
+----------+----------+
| |
WebSocket API / Reads
| |
v v
+--------------+ +--------------+
| Event | | Reconciliation|
| Processor | | Engine |
+------+-------+ +------+---------+
| |
+----------+----------+
|
v
+------------------+
| Persisted State |
| |
| Orders |
| Fills |
| Positions |
| Exposure |
+--------+---------+
|
v
+------------------+
| Strategy Engine |
+--------+---------+
|
v
+------------------+
| Risk Controls |
+--------+---------+
|
v
+------------------+
| Execution Engine |
+------------------+
The strategy is only one component.
The state layer is what allows the strategy to operate safely.
Backtesting should test this too
Most backtests focus on the strategy:
signal
β order
β profit
Production systems need more failure scenarios.
For example:
WebSocket disconnect
Partial fill
Duplicate event
Missed event
Delayed API response
Process restart
Order cancellation race
State mismatch
These can be simulated.
A good trading-system test should ask:
What happens if the world becomes inconsistent for 10 seconds?
That's often much more useful than another percentage-point improvement in a strategy backtest.
The bigger lesson
The most dangerous trading-system bug isn't necessarily a crash.
A crashed bot is obvious.
A bot that continues operating with the wrong view of reality is much harder to notice.
That is why state reconciliation deserves to be treated as a first-class subsystem.
The architecture should assume that:
- events can be delayed
- connections can fail
- fills can be partial
- processes can restart
- local state can become stale
- APIs can behave unexpectedly
And the system should know how to recover.
Final takeaway
When building a Polymarket trading bot, it's tempting to focus on the strategy:
signal
β buy
β sell
β profit
Production systems look more like:
market data
β event processing
β state
β reconciliation
β strategy
β risk
β execution
β persistence
β recovery
The strategy determines what you want to do.
The infrastructure determines whether you know what is actually happening.
That's why I would treat state reconciliation as a core part of any serious Polymarket trading system.
Fast execution matters.
Correct state matters more.
What I'd build next
The natural next component after reconciliation is a replayable Polymarket event stream.
If every order, fill, position change, and reconciliation event can be persisted and replayed, you can use the same infrastructure for:
- debugging
- backtesting
- incident analysis
- simulation
- strategy research
- production recovery
That's when the trading system starts becoming a reusable piece of infrastructure rather than a single-purpose bot.
This article focuses on production trading-system architecture and reliability. Proprietary strategy logic and parameters are intentionally omitted.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.