DEV Community

Cover image for Polymarket Partial Fills: How Automated Trading Bots Should Handle Them

Polymarket Partial Fills: How Automated Trading Bots Should Handle Them

An automated Polymarket trading bot requests 100.

Only 40 actually fill.

What should happen next?

A simple bot might treat the order as completed.

A production-oriented trading system cannot.

The system now has:

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

That changes the position, the exposure, the hedge, the next trade, and potentially the entire risk state.

This is why I think partial fills are a state-management problem, not just an order-management problem.

In this article, I'll walk through how I think about partial fills in an automated Polymarket trading system and why execution verification and reconciliation need to be separate parts of the architecture.


What is a partial fill?

A partial fill occurs when an order is executed for only part of its requested quantity.

For example:

Requested quantity: 100
Executed quantity:   40
Remaining quantity:  60
Enter fullscreen mode Exit fullscreen mode

The strategy wanted 100.

The market gave the system 40.

Those are two different facts.

A trading bot that continues to reason about the original 100 as though it were executed is now operating with incorrect state.

The first rule is therefore:

Requested quantity and executed quantity must be tracked separately.


Why partial fills are dangerous for trading bots

Imagine a strategy generates:

BUY 100
Enter fullscreen mode Exit fullscreen mode

The execution layer submits the order.

The market only fills:

40
Enter fullscreen mode Exit fullscreen mode

Now the system has to answer:

  1. Is the remaining 60 still resting?
  2. Should the order be cancelled?
  3. Should the remaining quantity be re-priced?
  4. Is the position supposed to be hedged?
  5. How much exposure actually exists?
  6. Does the strategy still want the trade?
  7. What happens if the remaining order never fills?

None of those questions can be answered correctly if the system stores only:

order.quantity = 100
Enter fullscreen mode Exit fullscreen mode

The actual execution state needs to be explicit.


The execution state should be more than FILLED / NOT FILLED

A common simplification is:

SUBMITTED
    ↓
FILLED
Enter fullscreen mode Exit fullscreen mode

Real execution is closer to:

INTENDED
    ↓
SUBMITTED
    ↓
ACCEPTED
    ↓
PARTIALLY_FILLED
    ↓
FILLED
Enter fullscreen mode Exit fullscreen mode

But there are other outcomes:

REJECTED
CANCELLED
FAILED
RETRYING
UNKNOWN
Enter fullscreen mode Exit fullscreen mode

And after execution:

FILLED
    ↓
TRANSACTION
    ↓
CONFIRMED
    ↓
SETTLED
Enter fullscreen mode Exit fullscreen mode

That is why I prefer thinking about execution as a state machine.

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

The exact state model will depend on the trading system, but the important idea is the same:

The trading system should represent what actually happened.


Partial fills change your risk

Consider a hedged trade.

Suppose the strategy expects:

Leg 1: 100
Leg 2: 100
Enter fullscreen mode Exit fullscreen mode

But Leg 1 only fills 40.

The system does not have a 100-unit exposure on Leg 1.

It has 40.

That means a second leg sized for 100 could create an unintended position.

Instead:

Requested Leg 1
       ↓
     100
       ↓
Actual Fill
       ↓
      40
       ↓
Hedge calculation
       ↓
      40
Enter fullscreen mode Exit fullscreen mode

This is why hedge logic should be based on actual execution, not intended execution.

The distinction is simple:

Requested size ≠ Filled size
Enter fullscreen mode Exit fullscreen mode

and:

Filled size → actual exposure
Enter fullscreen mode Exit fullscreen mode

The bot needs to track residual quantity

After a partial fill:

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

The remaining quantity should be explicit.

Conceptually:

remaining = requested - filled
Enter fullscreen mode Exit fullscreen mode

But the trading system also needs to know what the remaining quantity means.

It could be:

60 resting on the book
Enter fullscreen mode Exit fullscreen mode

or:

60 cancelled
Enter fullscreen mode Exit fullscreen mode

or:

60 unknown
Enter fullscreen mode Exit fullscreen mode

or:

60 requiring a retry
Enter fullscreen mode Exit fullscreen mode

The quantity alone is not enough.

The state of the remaining quantity matters.


What happens when the second leg doesn't fill?

This is where partial fills become particularly important for arbitrage and hedged strategies.

Suppose the intended trade is:

Leg 1 → 100
Leg 2 → 100
Enter fullscreen mode Exit fullscreen mode

But the result is:

Leg 1 → 100 filled
Leg 2 →   0 filled
Enter fullscreen mode Exit fullscreen mode

The system now owns an unintended exposure.

A naive implementation might simply retry Leg 2.

That can be dangerous if the market moved.

The price may no longer support the original trade.

A more deliberate execution flow is:

Leg 1 executed
      ↓
Measure actual fill
      ↓
Check current market
      ↓
Check risk
      ↓
Attempt hedge
      ↓
Verify hedge
      ↓
Reconcile final position
Enter fullscreen mode Exit fullscreen mode

The system should be able to choose among actions such as:

HEDGE
REPRICE
CANCEL
UNWIND
PAUSE
Enter fullscreen mode Exit fullscreen mode

The correct action depends on the strategy and risk policy.


Partial fills and position reconciliation

This is where execution state meets account state.

Suppose your local system says:

Expected position: +100
Enter fullscreen mode Exit fullscreen mode

But execution records say:

Filled: +40
Enter fullscreen mode Exit fullscreen mode

And the account currently reports:

Actual position: +40
Enter fullscreen mode Exit fullscreen mode

The system is consistent.

Now consider:

Expected position: +100
Filled: +40
Actual position: +25
Enter fullscreen mode Exit fullscreen mode

That is a different problem.

Something else happened between execution and account state.

Now the trading system needs reconciliation.

A useful architecture is:

              ORDER
                ↓
             FILL(S)
                ↓
          Local Position
                ↓
        Position Reconciler
                ↓
          Remote Position
                ↓
             Verified
Enter fullscreen mode Exit fullscreen mode

The point is not to assume that one event tells you the whole story.


Why WebSocket events aren't enough

Real-time events are essential for low-latency systems.

But a trading bot should not assume that every event will arrive exactly once and in perfect order.

Possible problems include:

  • disconnects
  • missed events
  • duplicated events
  • delayed events
  • application restart
  • API inconsistencies

That means the system needs two complementary mechanisms:

Real-time updates
       +
Reconciliation
Enter fullscreen mode Exit fullscreen mode

Real-time events keep the system fast.

Reconciliation helps it recover when reality and local state diverge.


A safer recovery sequence

Imagine a partial-fill event happens during a WebSocket disconnect.

The bot reconnects.

It should not automatically decide:

“Everything is fine. Continue trading.”

A safer flow is:

WebSocket disconnect
        ↓
Trading = PAUSED
        ↓
Reconnect
        ↓
Rebuild / reload local state
        ↓
Reconcile orders
        ↓
Reconcile fills
        ↓
Reconcile positions
        ↓
Verify exposure
        ↓
Verify risk
        ↓
Resume trading
Enter fullscreen mode Exit fullscreen mode

The key distinction is:

Reconnect is a network operation. Recovery is a trading-system operation.


Unknown execution state needs its own path

One of the most dangerous states is:

UNKNOWN
Enter fullscreen mode Exit fullscreen mode

Suppose the application submits an order.

The network fails before the application receives the final response.

Now the system does not know whether the order:

  • was rejected
  • was accepted
  • partially filled
  • completely filled

Immediately submitting the same order again could create duplicate exposure.

So instead:

UNKNOWN
   ↓
Do not blindly retry
   ↓
Query / reconcile
   ↓
Determine actual state
   ↓
Continue according to policy
Enter fullscreen mode Exit fullscreen mode

This is another reason I prefer an explicit execution-verification layer.


Execution verification

I’ve been building a separate Polymarket Execution Verifier around this problem.

The idea is to verify execution across multiple layers:

Order
  ↓
Fill
  ↓
Transaction
  ↓
Settlement
  ↓
Position
Enter fullscreen mode Exit fullscreen mode

Instead of allowing the trading bot to assume:

ORDER = DONE
Enter fullscreen mode Exit fullscreen mode

the verifier asks:

What actually happened?
Enter fullscreen mode Exit fullscreen mode

Possible results include:

KNOWN
UNKNOWN
FAILED
SETTLED
INCONSISTENT
Enter fullscreen mode Exit fullscreen mode

This makes execution verification an explicit component rather than something hidden inside a strategy.


Partial fills in a control-plane architecture

I think of the broader system like this:

                 POLYMARKET
                      │
                 Market Data
                      │
                      ▼
              ┌───────────────┐
              │ Trading Bot   │
              │               │
              │ Strategy      │
              │ Execution     │
              └───────┬───────┘
                      │
                      ▼
             Execution Verifier
                      │
            ┌─────────┴─────────┐
            ▼                   ▼
        Fill State          TX State
            │                   │
            └─────────┬─────────┘
                      ▼
                 Position
                      │
                      ▼
               Control Plane
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
      Risk         Health      Reconciliation
        │             │             │
        └─────────────┼─────────────┘
                      ▼
                   Recovery
Enter fullscreen mode Exit fullscreen mode

The goal is to separate responsibilities.

Strategy

Decides what it wants to trade.

Execution

Attempts to place the order.

Execution verifier

Determines what actually happened.

Control plane

Determines whether the system should continue operating.

That separation becomes increasingly valuable as the trading system grows.


The most important invariant

For me, the key invariant is:

Actual Position
    =
Verified Executions
    =
Risk Calculation Input
Enter fullscreen mode Exit fullscreen mode

At least, that should be the goal.

If those three things disagree, the system should stop treating its local state as trustworthy.

That leads to a simple operational rule:

When critical state is uncertain, reduce or stop new risk until state is verified.


Testing partial fills

Partial-fill handling should be tested explicitly.

For example:

Scenario Expected behavior
Full fill Mark execution complete
Partial fill Track actual filled quantity
Remaining order open Track residual quantity
Remaining order cancelled Recalculate state
Partial hedge Recalculate exposure
Unknown execution Reconcile before retry
Duplicate event Avoid double-counting
WebSocket reconnect Reconcile
Position mismatch Block trading
Settlement mismatch Escalate / reconcile

The happy path is easy.

The failure paths are where the trading system needs engineering.


Building around the strategy

This is why I've become increasingly interested in the infrastructure around automated Polymarket trading.

A strategy can answer:

What should I trade?

But that doesn't answer:

How much actually filled?

or:

What position do I really have?

or:

Is it safe to place another order?

Those questions belong to the execution, state, risk, and control layers.

The architecture becomes:

Strategy
   ↓
Execution
   ↓
Verification
   ↓
State
   ↓
Risk
   ↓
Monitoring
   ↓
Recovery
Enter fullscreen mode Exit fullscreen mode

That is the direction I'm exploring with the Polymarket Trading Bot, Execution Verifier, and Trading Control Plane projects.


Related projects

Polymarket Trading Bot

Automated trading, execution, risk management, and backtesting.

https://github.com/casatrickdev/polymarket-trading-bot

Polymarket Execution Verifier

Verify execution across orders, fills, transactions, settlement, and position state.

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

Polymarket Trading Control Plane

Monitoring, state reconciliation, risk controls, health, alerts, and recovery.

https://github.com/casatrickdev/polymarket-trading-control-plane

Together:

Trading Bot
    ↓
Execution
    ↓
Partial Fill Handling
    ↓
Execution Verification
    ↓
State Reconciliation
    ↓
Risk / Monitoring
    ↓
Recovery
Enter fullscreen mode Exit fullscreen mode

Final takeaway

A partial fill looks like a small execution detail.

It isn't.

Once an automated trading system starts managing real positions, a partial fill changes:

  • actual position
  • remaining quantity
  • exposure
  • hedge requirements
  • risk
  • execution state
  • what the strategy should do next

The key is not to make the system assume that an intended trade became a completed trade.

Instead:

Intent
  ↓
Execution
  ↓
Actual Fill
  ↓
Verified State
  ↓
Position
  ↓
Risk
Enter fullscreen mode Exit fullscreen mode

A trading bot becomes much easier to reason about when it knows the difference between what it wanted, what it executed, and what it actually owns.

That's the problem I'm exploring with execution verification and trading-system infrastructure for Polymarket.

Top comments (0)