close

DEV Community

BornToWin
BornToWin

Posted on Originally published at guskarls.Medium

Polymarket Bot: Practical Order Execution, Imbalance, and Liquidity

Building a Polymarket bot is not simply about finding a trading signal and sending an order.

The real challenge starts after the signal.

A bot may correctly identify an opportunity, but the execution can still fail because of:

  • Poor liquidity
  • Order-book imbalance
  • Slippage
  • Stale market data
  • Bad order timing
  • Incorrect order type
  • Partial fills
  • Excessive market impact
  • Position imbalance

After working through these problems, I started looking at the Polymarket bot as two separate systems:

Strategy

Should I trade?

Execution

How should I trade?

This article focuses on the second part.

What a Polymarket Bot Actually Needs to Decide

Suppose the strategy generates:

BUY YES
Enter fullscreen mode Exit fullscreen mode

That is not enough information for the execution engine.

The bot still needs to determine:

Price
Size
Order type
Timing
Available liquidity
Expected slippage
Current position
Current exposure
Enter fullscreen mode Exit fullscreen mode

The same signal can produce completely different results depending on how it is executed.

That is why I consider order execution one of the most important parts of a Polymarket bot.

Limit Orders vs Aggressive Execution

A limit order gives the bot control over the maximum price it is willing to pay.

For example:

Best bid = 0.48
Best ask = 0.51
Enter fullscreen mode Exit fullscreen mode

The strategy might decide:

Maximum entry = 0.49
Enter fullscreen mode Exit fullscreen mode

The bot places:

BUY YES @ 0.49
Enter fullscreen mode Exit fullscreen mode

Now it waits for a seller.

This protects the entry price, but there is a cost.

The market might move:

0.50
  ↓
0.52
  ↓
0.55
  ↓
0.58
Enter fullscreen mode Exit fullscreen mode

The order never fills.

The bot protected itself from paying too much, but the opportunity disappeared.

This creates an important execution question:

Is missing the trade worse than paying the spread and taking available liquidity?

There is no universal answer.

It depends on the expected edge, market conditions, liquidity, and strategy.

Aggressive Execution

Sometimes the signal has a very short lifetime.

For example:

Signal
  ↓
Market moves
  ↓
Edge disappears
Enter fullscreen mode Exit fullscreen mode

Waiting for a passive order can cause the bot to miss the opportunity.

Aggressive execution can solve the timing problem, but it introduces other costs:

  • Spread
  • Slippage
  • Market impact
  • Liquidity consumption

So one useful rule is:

Expected Edge > Expected Execution Cost
Enter fullscreen mode Exit fullscreen mode

If the edge is smaller than the expected execution cost, the trade may no longer be attractive.

FOK and FAK

Execution behavior matters too.

FOK

FOK means:

Fill or Kill

The order must be completely filled immediately or cancelled.

Example:

BUY 100

Available = 100

→ Fill 100
Enter fullscreen mode Exit fullscreen mode

But:

BUY 100

Available = 60

→ Cancel
Enter fullscreen mode Exit fullscreen mode

This can be useful when partial execution would make the strategy invalid.

FAK

FAK means:

Fill and Kill

The immediately available quantity is filled and the remaining quantity is cancelled.

Example:

BUY 100

Available = 60

→ Fill 60
→ Cancel remaining 40
Enter fullscreen mode Exit fullscreen mode

FAK can make sense when partial execution is still useful.

The important point is that the Polymarket bot should choose the execution behavior according to the strategy.

Don't Look Only at the Last Price

A common mistake is to build a Polymarket bot around the last traded price.

The order book contains much more information.

For example:

BIDS

0.48 → 100
0.47 → 300
0.46 → 500

ASKS

0.52 → 50
0.53 → 100
0.54 → 400
Enter fullscreen mode Exit fullscreen mode

Now the bot can evaluate:

  • Best bid
  • Best ask
  • Spread
  • Depth
  • Available liquidity
  • Expected execution price
  • Potential price impact

The order book should be part of the bot's current market state.

Order-Book Imbalance

One of the practical metrics I look at is order-book imbalance.

A simple calculation is:

imbalance =
(bidVolume - askVolume)
/
(bidVolume + askVolume)
Enter fullscreen mode Exit fullscreen mode

For example:

bidVolume = 800
askVolume = 200

imbalance =
(800 - 200) / (800 + 200)

= 0.60
Enter fullscreen mode Exit fullscreen mode

There is significantly more visible bid volume than ask volume within the selected depth.

But there is an important warning:

Imbalance is not automatically a BUY signal.

This is one of the easiest mistakes to make when developing a Polymarket bot.

An imbalance can disappear quickly.

Orders can be:

  • Cancelled
  • Replaced
  • Temporary
  • Concentrated at one price
  • Too far from the current execution price

So I would not build a strategy like:

if (imbalance > 0.5) {
    buy();
}
Enter fullscreen mode Exit fullscreen mode

and assume the result will be reliable.

Define the Depth

The bot should have a consistent definition of imbalance.

For example:

Best bid
+
5 bid levels
Enter fullscreen mode Exit fullscreen mode

versus:

Best ask
+
5 ask levels
Enter fullscreen mode Exit fullscreen mode

Then calculate the imbalance.

A simple implementation could be:

function calculateImbalance(
  bidVolume: number,
  askVolume: number
) {
  const total = bidVolume + askVolume;

  if (total === 0) {
    return 0;
  }

  return (bidVolume - askVolume) / total;
}
Enter fullscreen mode Exit fullscreen mode

The exact depth depends on the strategy.

What matters is that the bot consistently measures the same thing.

Persistence Matters

A single imbalance snapshot can be misleading.

Imagine:

t0 → +0.70
t1 → +0.05
t2 → -0.30
Enter fullscreen mode Exit fullscreen mode

The imbalance disappeared almost immediately.

Now compare:

t0 → +0.70
t1 → +0.68
t2 → +0.72
t3 → +0.65
Enter fullscreen mode Exit fullscreen mode

The second condition is more interesting.

Why?

Because the imbalance persisted.

So instead of measuring only:

Imbalance Magnitude
Enter fullscreen mode Exit fullscreen mode

the bot can also measure:

Imbalance Persistence
Enter fullscreen mode Exit fullscreen mode

This gives the execution engine more context.

Imbalance Needs Context

I would never treat imbalance as an isolated signal.

Consider:

Imbalance ↑
Price ↑
Trade flow ↑
Enter fullscreen mode Exit fullscreen mode

That is different from:

Imbalance ↑
Price ↓
Trade flow ↓
Enter fullscreen mode Exit fullscreen mode

The bot should consider multiple pieces of market state:

  • Order-book imbalance
  • Price movement
  • Recent trades
  • Spread
  • Liquidity
  • Depth
  • Time remaining
  • Current position
  • Existing orders

The goal is not to find one magic indicator.

The goal is to build a better market-state model.

Large Orders and Slippage

Now consider a larger order.

Suppose the ask side contains:

0.50 → 20
0.51 → 30
0.52 → 50
0.53 → 100
Enter fullscreen mode Exit fullscreen mode

The bot wants to buy 150.

It cannot assume:

150 × 0.50
Enter fullscreen mode Exit fullscreen mode

The actual execution could be:

20 @ 0.50
30 @ 0.51
50 @ 0.52
50 @ 0.53
Enter fullscreen mode Exit fullscreen mode

The average execution price is now much higher.

This is why a Polymarket bot should estimate:

  • Available depth
  • Expected average execution price
  • Slippage
  • Price impact

before submitting larger orders.

When the Bot Pushes the Market

Large marketable orders can consume multiple levels of liquidity.

Before:

ASK

0.50 → 100
0.51 → 100
0.52 → 200
Enter fullscreen mode Exit fullscreen mode

After a large buy:

ASK

0.52 → 200
Enter fullscreen mode Exit fullscreen mode

The best ask moved because liquidity was consumed.

But I would not automatically call this momentum.

There is a difference between:

Liquidity consumption
Enter fullscreen mode Exit fullscreen mode

and:

Other traders repricing the market
Enter fullscreen mode Exit fullscreen mode

That distinction is important when interpreting order flow.

Don't Let the Bot Chase Its Own Order

Consider this sequence:

Signal
  ↓
Large order
  ↓
Liquidity consumed
  ↓
Price moves
  ↓
Bot continues buying
  ↓
Average entry gets worse
Enter fullscreen mode Exit fullscreen mode

The original signal may still be correct.

The execution is what failed.

This is why order size should consider:

Available Depth
+
Expected Edge
+
Maximum Slippage
+
Current Exposure
Enter fullscreen mode Exit fullscreen mode

A good execution engine should know when to stop.

Order Timing

One of the most overlooked parts of a Polymarket bot is timing.

A signal is generated at one point in time.

The order may be submitted later.

The market may have changed in between.

For example:

10:00:00.000
Signal generated

10:00:00.100
Order created

10:00:00.180
Order submitted

10:00:00.250
Order matched
Enter fullscreen mode Exit fullscreen mode

The market state at 10:00:00.000 may no longer exist at 10:00:00.250.

This creates a signal-to-execution race condition.

Revalidate Before Execution

Instead of:

Signal
  ↓
Submit
Enter fullscreen mode Exit fullscreen mode

I prefer:

Signal
  ↓
Revalidate Market
  ↓
Revalidate Price
  ↓
Revalidate Liquidity
  ↓
Revalidate Position
  ↓
Revalidate Risk
  ↓
Submit
Enter fullscreen mode Exit fullscreen mode

The closer the execution is to the signal, the less likely the bot is to act on stale information.

Split and Merge

Split and merge are another interesting part of building a Polymarket bot.

They are especially relevant to inventory management.

Conceptually:

Collateral
   ↓
 SPLIT
   ↓
 YES + NO
Enter fullscreen mode Exit fullscreen mode

This can be useful when a strategy needs inventory on both outcomes.

Now consider a bot holding:

100 YES
100 NO
Enter fullscreen mode Exit fullscreen mode

The matching positions can potentially be merged:

100 YES
   +
100 NO
   ↓
100 collateral
Enter fullscreen mode Exit fullscreen mode

If the bot instead has:

100 YES
50 NO
Enter fullscreen mode Exit fullscreen mode

only the matching amount can be merged:

50 YES
   +
50 NO
   ↓
50 collateral
Enter fullscreen mode Exit fullscreen mode

The remaining YES position is still open.

Split and Merge Are Inventory Tools

I don't think of split and merge as direct trading signals.

I think of them as:

Inventory Management
       +
Position Management
Enter fullscreen mode Exit fullscreen mode

The bot can ask:

Do I need YES + NO inventory?

→ Consider SPLIT
Enter fullscreen mode Exit fullscreen mode

Or:

Do I have matching YES + NO?

→ Consider MERGE
Enter fullscreen mode Exit fullscreen mode

The decision still needs to consider the current position, costs, timing, and execution conditions.

The Order Lifecycle

Submitting an order is not the end of the execution process.

The bot needs to track what happens after submission.

For example:

NEW
  ↓
SUBMITTED
  ↓
OPEN
  ↓
PARTIALLY FILLED
  ↓
FILLED
Enter fullscreen mode Exit fullscreen mode

Or:

SUBMITTED
  ↓
REJECTED
Enter fullscreen mode Exit fullscreen mode

Or:

OPEN
  ↓
MARKET MOVES
  ↓
CANCEL
  ↓
REPLACE
Enter fullscreen mode Exit fullscreen mode

This is why I prefer treating order management as a state machine.

Example states:

NEW
SUBMITTED
OPEN
PARTIALLY_FILLED
FILLED
CANCELLED
REJECTED
Enter fullscreen mode Exit fullscreen mode

Explicit states make the execution engine much easier to reason about.

My Polymarket Bot Execution Checklist

Before submitting an important order, I want to check:

[ ] Market is active
[ ] Market data is fresh
[ ] Order book is fresh
[ ] Spread is acceptable
[ ] Liquidity is sufficient
[ ] Expected slippage is acceptable
[ ] Imbalance is meaningful
[ ] Imbalance is persistent
[ ] Signal is still valid
[ ] Position size is acceptable
[ ] Exposure limit is not exceeded
[ ] Price is still valid
[ ] Order size is valid
[ ] Execution type is appropriate
Enter fullscreen mode Exit fullscreen mode

Only after those checks:

PLACE ORDER
Enter fullscreen mode Exit fullscreen mode

Complete Polymarket Bot Execution Flow

The practical execution architecture looks like:

MARKET DATA
     ↓
ORDER BOOK
     ↓
TWAP STATE
     ↓
TRADE FLOW
     ↓
IMBALANCE
     ↓
STRATEGY
     ↓
SIGNAL
     ↓
FRESHNESS CHECK
     ↓
LIQUIDITY CHECK
     ↓
SLIPPAGE CHECK
     ↓
POSITION CHECK
     ↓
EXECUTION TYPE
     ↓
ORDER SUBMIT
     ↓
FILL MONITOR
     ↓
CANCEL / REPLACE
     ↓
POSITION UPDATE
Enter fullscreen mode Exit fullscreen mode

This is very different from:

Signal → Buy
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

The strategy is only half of a Polymarket bot.

The other half is execution.

A serious Polymarket bot needs to answer two questions:

Should I trade?

and:

How should I execute?

Limit orders provide price control.

Aggressive execution provides speed.

FOK provides all-or-nothing execution.

FAK allows partial immediate execution.

Order-book imbalance provides useful context, but it should not automatically become a BUY or SELL signal.

Split can help create inventory.

Merge can help manage matching positions.

And execution timing can be just as important as the original trading signal.

The execution process I care about is:

Signal
  ↓
Validate
  ↓
Measure Liquidity
  ↓
Check Imbalance
  ↓
Estimate Impact
  ↓
Choose Order Type
  ↓
Execute
  ↓
Monitor
  ↓
Reconcile
Enter fullscreen mode Exit fullscreen mode

That's where a Polymarket bot becomes an actual execution system rather than just a strategy script.

If you're building a Polymarket bot, the trading signal is only the beginning.

The real engineering challenge is what happens between:

"I found an opportunity"
Enter fullscreen mode Exit fullscreen mode

and:

"The order was actually executed."
Enter fullscreen mode Exit fullscreen mode

Automated trading involves financial risk. No execution technique guarantees profit, and historical or simulated results do not guarantee future performance.

Top comments (0)