DEV Community

Cover image for Polymarket Execution Verification: When “Filled” Doesn’t Mean Settled

Polymarket Execution Verification: When “Filled” Doesn’t Mean Settled

A Polymarket trading bot can receive a successful execution response and still not know enough to safely continue trading.

That sounds like a small implementation detail.

It isn't.

In a production trading system, there is a big difference between:

"I sent the order."

"I received a fill."

"The fill has a transaction."

"The transaction is confirmed."

"The execution is settled."

"My position matches reality."
Enter fullscreen mode Exit fullscreen mode

Those are different states.

If your bot treats them as one state called FILLED, eventually you get a system that believes it owns one position while the account says something else.

This is why execution verification deserves its own layer.

I built a small Polymarket execution-verification project around this idea:

Don't let a trading system assume an execution is complete before the relevant state has been verified.

GitHub: Polymarket Execution Verifier


The problem with treating FILLED as the end

Imagine the strategy decides to buy 100 shares.

The bot submits:

BUY 100
Enter fullscreen mode Exit fullscreen mode

Then it receives an execution update:

FILLED
Enter fullscreen mode Exit fullscreen mode

A naive implementation does:

filled = true
position += 100
continue trading
Enter fullscreen mode Exit fullscreen mode

That is dangerous.

The bot has made several assumptions at once:

  • the requested quantity was actually matched
  • the execution event was complete
  • the settlement transaction is known
  • the transaction was successfully processed
  • the external position reflects the expected result
  • no relevant event was missed

Those assumptions can fail independently.

A safer trading system treats execution as a state transition rather than a boolean.


Order → Fill → Transaction → Confirmation → Settlement → Position

The execution lifecycle I use is:

INTENDED
   ↓
SUBMITTED
   ↓
ACCEPTED
   ↓
MATCHED
   ↓
FILLED
   ↓
TX_PENDING
   ↓
CONFIRMED
   ↓
SETTLED
   ↓
POSITION VERIFIED
Enter fullscreen mode Exit fullscreen mode

There are also failure and uncertainty states:

REJECTED
CANCELLED
FAILED
RETRYING
UNKNOWN
INCONSISTENT
Enter fullscreen mode Exit fullscreen mode

The important part is that FILLED is not necessarily the final state your risk engine should trust.

Polymarket's current CLOB tooling also distinguishes trade completion from simply observing a fill: the current client logic considers a trade resolved when it reaches a terminal outcome, such as having a settlement transaction hash or having failed. The client can also expose transaction hashes when available and trade IDs that can be followed while a hash is not yet available.

That distinction is exactly what an execution verifier should make explicit.


Why execution state becomes uncertain

There are several ways a trading bot can end up with incomplete execution information.

1. The order was matched, but transaction information is not available yet

A matching event tells you something important happened.

It does not necessarily mean every downstream piece of information is already available to your application.

For example:

Order
  ↓
Matched
  ↓
Fill observed
  ↓
Transaction information unavailable
Enter fullscreen mode Exit fullscreen mode

The correct state is not:

DONE
Enter fullscreen mode Exit fullscreen mode

It is closer to:

TX_PENDING
Enter fullscreen mode Exit fullscreen mode

or:

UNKNOWN
Enter fullscreen mode Exit fullscreen mode

depending on what the system actually knows.

Your execution layer should preserve that uncertainty instead of hiding it.


2. Partial execution

Suppose you intended:

100 shares
Enter fullscreen mode Exit fullscreen mode

but only 40 were matched.

Your bot now has:

Requested: 100
Matched:    40
Remaining:  60
Enter fullscreen mode Exit fullscreen mode

That remaining quantity cannot simply disappear from your accounting.

The execution system needs to know whether the remaining 60:

  • are still active
  • were cancelled
  • expired
  • failed
  • were never accepted
  • require another verification step

This is why requested quantity and executed quantity should always be separate fields.

For example:

{
  "requested_size": 100,
  "matched_size": 40,
  "remaining_size": 60,
  "execution_state": "PARTIAL"
}
Enter fullscreen mode Exit fullscreen mode

The exact data model can vary, but the principle should remain the same.


3. The transaction is confirmed, but the position still isn't verified

This is one of the most important boundaries.

Consider:

FILLED
   ↓
TX CONFIRMED
Enter fullscreen mode Exit fullscreen mode

A developer may immediately update the internal position:

local_position += executed_size
Enter fullscreen mode Exit fullscreen mode

But the trading system still has another question:

Does the external account state actually match what I expected?

For example:

Expected position: 125.5

Observed position: 100.0
Enter fullscreen mode Exit fullscreen mode

The transaction succeeded.

The internal state is still wrong.

This is where execution verification connects directly to position reconciliation.

A trading bot should not confuse:

transaction verified
Enter fullscreen mode Exit fullscreen mode

with:

position verified
Enter fullscreen mode Exit fullscreen mode

Those are different checks.


4. A WebSocket event can be missed

Real-time trading systems depend heavily on event streams.

That creates another failure mode.

Imagine:

Event 101 received
Event 102 received
[connection drops]
Event 103 happens
Event 104 happens
connection restored
Enter fullscreen mode Exit fullscreen mode

Your application reconnects.

But reconnecting the socket does not magically reconstruct the events that were missed.

You now have:

local state != external state
Enter fullscreen mode Exit fullscreen mode

The correct recovery sequence is:

DISCONNECT
   ↓
PAUSE
   ↓
RECONNECT
   ↓
REBUILD STATE
   ↓
RECONCILE
   ↓
VERIFY
   ↓
RESUME
Enter fullscreen mode Exit fullscreen mode

This is why execution verification cannot be implemented as a single API callback.

It belongs inside a larger state-management system.


Execution verification should be explicit

Instead of:

if order.filled {
    update_position();
    continue_trading();
}
Enter fullscreen mode Exit fullscreen mode

use an explicit state machine.

For example:

enum ExecutionState {
    Intended,
    Submitted,
    Accepted,
    Matched,
    Filled,
    TxPending,
    Confirmed,
    Settled,
    PositionVerified,

    Rejected,
    Cancelled,
    Failed,
    Retrying,
    Unknown,
    Inconsistent,
}
Enter fullscreen mode Exit fullscreen mode

Now the rest of the system can react to the state.

For example:

FILLED
    ↓
Does transaction information exist?
    ↓
No
    ↓
TX_PENDING
Enter fullscreen mode Exit fullscreen mode

Then:

TX_PENDING
    ↓
Transaction confirmed?
    ↓
Yes
    ↓
CONFIRMED
Enter fullscreen mode Exit fullscreen mode

Then:

CONFIRMED
    ↓
Position matches expected state?
    ↓
No
    ↓
INCONSISTENT
Enter fullscreen mode Exit fullscreen mode

This is much safer than a single filled=true flag.


The verifier should separate facts from assumptions

A useful execution record might contain:

strategy_decision_id
internal_execution_id

polymarket_order_id
trade_id

market_id
token_id
side

requested_quantity
matched_quantity

order_timestamp
fill_timestamp

transaction_hash
confirmation_timestamp

execution_state
position_state

verification_timestamp
Enter fullscreen mode Exit fullscreen mode

The important property is that each value represents something actually observed.

For example:

requested_quantity = 100
matched_quantity   = 40
Enter fullscreen mode Exit fullscreen mode

is different from assuming:

matched_quantity = requested_quantity
Enter fullscreen mode Exit fullscreen mode

Likewise:

execution_state = CONFIRMED
Enter fullscreen mode Exit fullscreen mode

should not automatically imply:

position_state = VERIFIED
Enter fullscreen mode Exit fullscreen mode

Good trading infrastructure makes those boundaries visible.


What should happen when verification fails?

This is where execution verification becomes a risk-control problem.

Suppose the verifier reaches:

INCONSISTENT
Enter fullscreen mode Exit fullscreen mode

The strategy layer should not simply continue because it still has a signal.

The control flow should look more like:

Execution uncertain
       ↓
Pause affected trading
       ↓
Query authoritative state
       ↓
Reconcile orders / fills / positions
       ↓
Verify risk
       ↓
    Healthy?
   ↙       ↘
 YES       NO
 ↓          ↓
RESUME     PAUSE
Enter fullscreen mode Exit fullscreen mode

The trading decision is therefore not just:

Should I buy?
Enter fullscreen mode Exit fullscreen mode

It also becomes:

Is the system in a state where buying is safe?
Enter fullscreen mode Exit fullscreen mode

That is a very different engineering problem.


A useful execution verifier architecture

The project I am working on separates verification into layers:

                 Strategy
                    │
                    ▼
              Execution Request
                    │
                    ▼
              Order Submission
                    │
                    ▼
            ┌──────────────────┐
            │   Execution      │
            │    Verifier      │
            └──────────────────┘
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
      Order        Fill       Transaction
        │           │           │
        └───────────┼───────────┘
                    ▼
               Settlement
                    │
                    ▼
            Position Verification
                    │
                    ▼
                 Risk Layer
                    │
                    ▼
             Resume / Pause
Enter fullscreen mode Exit fullscreen mode

This gives the system a clear boundary:

the strategy produces an intention; the infrastructure determines whether reality matches that intention.


Execution verification vs position reconciliation

These two concepts are closely related, but they solve different problems.

Execution verification

Question:

Did the intended trade actually reach a verified terminal execution state?

It follows:

Order
→ Fill
→ Transaction
→ Confirmation
→ Settlement
Enter fullscreen mode Exit fullscreen mode

Position reconciliation

Question:

Does the account's actual position match the position my system believes it has?

It follows:

Expected State
      ↓
External State
      ↓
Compare
      ↓
Match / Mismatch
Enter fullscreen mode Exit fullscreen mode

You need both.

For example:

Execution: VERIFIED
Position:  MISMATCH
Enter fullscreen mode Exit fullscreen mode

is completely possible.

Likewise:

Execution: UNKNOWN
Position:  temporarily unchanged
Enter fullscreen mode Exit fullscreen mode

is also possible.

The system should retain both pieces of information.


Why this matters for automated trading

A strategy can be statistically correct and still lose control of the system.

The failure may happen after the signal.

For example:

Signal
  ↓
Order
  ↓
Partial Fill
  ↓
WebSocket Gap
  ↓
Incorrect Local State
  ↓
Incorrect Position
  ↓
Incorrect Risk Calculation
  ↓
New Order
Enter fullscreen mode Exit fullscreen mode

The original strategy wasn't necessarily the problem.

The execution state was.

This is why production trading infrastructure needs more than:

market data
+
strategy
+
order placement
Enter fullscreen mode Exit fullscreen mode

It needs:

market data
+
strategy
+
execution
+
verification
+
reconciliation
+
risk
+
recovery
+
observability
Enter fullscreen mode Exit fullscreen mode

When should the bot resume trading?

This is the final question.

Suppose the bot reconnects after losing its market-data connection.

Should it immediately resume?

No state machine should answer that with:

socket_connected == true
Enter fullscreen mode Exit fullscreen mode

A safer condition is closer to:

connection healthy
AND
market data fresh
AND
orders reconciled
AND
fills reconciled
AND
positions verified
AND
exposure verified
AND
risk limits healthy
Enter fullscreen mode Exit fullscreen mode

Only then should the system move back toward:

TRADING
Enter fullscreen mode Exit fullscreen mode

This is also where an execution verifier connects to a broader trading control plane.

The verifier answers:

What actually happened?

The reconciliation layer answers:

Does my state match reality?

The risk layer answers:

Is continuing safe according to configured limits?

The control plane coordinates the result.


Building the execution verifier

My current project focuses on making these execution states explicit instead of burying them inside strategy code.

Repository:

github.com/casatrickdev/polymarket-execution-verifier

The project is intentionally focused on one problem:

Verify Polymarket execution
across
order → fill → transaction → confirmation → settlement → position
Enter fullscreen mode Exit fullscreen mode

That makes it easier to integrate with the rest of a production trading stack.

The broader architecture connects this with:

  • a Polymarket trading bot
  • position reconciliation
  • trading monitoring
  • risk controls
  • recovery logic
  • operational control

Projects:

Polymarket Trading Bot

Polymarket Trading Control Plane


Practical execution-verification checklist

Before allowing a trading bot to treat an execution as complete, I want to be able to answer:

[ ] Was the order accepted?
[ ] Was it matched?
[ ] How much actually filled?
[ ] Is the remaining quantity known?
[ ] Is the relevant transaction known?
[ ] Has the transaction reached the expected terminal state?
[ ] Is settlement verified?
[ ] Does the external position match expected state?
[ ] Is exposure correct?
[ ] Are risk limits still valid?
[ ] Is there any unresolved execution uncertainty?
Enter fullscreen mode Exit fullscreen mode

If one of those answers is unknown, the system should know that it is unknown.

That sounds obvious.

In production, it is one of the differences between a bot that places trades and a trading system that can actually be trusted.


Final takeaway

A Polymarket trading bot should not think:

FILLED = DONE
Enter fullscreen mode Exit fullscreen mode

A better mental model is:

INTENDED
   ↓
SUBMITTED
   ↓
MATCHED
   ↓
FILLED
   ↓
TRANSACTION VERIFIED
   ↓
SETTLED
   ↓
POSITION VERIFIED
   ↓
RISK VERIFIED
   ↓
SAFE TO CONTINUE
Enter fullscreen mode Exit fullscreen mode

The important question is not only whether an order was filled.

It is:

Can the system prove that its current trading state matches reality?

That is the reason for building an execution-verification layer.


Project

Polymarket Execution Verifier

Verify execution across:

ORDER
FILL
TRANSACTION
CONFIRMATION
SETTLEMENT
POSITION
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/casatrickdev/polymarket-execution-verifier


Related reading

  • Polymarket Trading Bot: Execution, Risk Management, and Backtesting
  • Polymarket Partial Fills: How Automated Trading Bots Should Handle Them
  • Polymarket Position Reconciliation: Keeping Trading Bot State in Sync
  • Polymarket WebSocket Reconnects: Rebuilding Trading State Safely
  • Polymarket Trading Bot Monitoring: State, Risk and Recovery

Top comments (0)