Polymarket Bot Tutorial · Chapter 17 of 32
Use Polymarket order book imbalance as a short-term price signal: bid-ask volume ratio, microprice computation, signal half-life, and when imbalance bots beat random execution.
What this chapter covers
Order-book imbalance is the ratio of buy-side depth to sell-side depth on the limit order book. On Polymarket it has a real but short-lived predictive edge - typically 5-30 seconds before the mid moves. This chapter is the computation pattern and the conditions under which the signal lies.
- What order book imbalance is
- Microprice computation
- Imbalance as a directional signal
- Signal half-life on Polymarket
- When imbalance signals lie
- Code: compute imbalance every WS tick
What order book imbalance is
Order book imbalance is the ratio of total buy-side depth to total sell-side depth on the limit order book. Computed at the top-N levels (commonly N=5), it captures aggregate trader pressure that the mid-price has not yet reflected.
Formula: imbalance = (Σ bid[i].price × bid[i].size for i in [0..N]) − (Σ ask[i].price × ask[i].size) / (Σ both). Range -1 to +1; positive means more buy pressure, negative means more sell pressure.
The signal is empirically real on Polymarket but noisy. A single whale can fake-print imbalance for 30-60 seconds before being arbed out. Useful as one feature among several, dangerous as a sole trigger.
Microprice computation
The microprice is a refinement of the simple mid: a weighted average of best bid and best ask, weighted by their respective sizes.
microprice = (best_bid × ask_size + best_ask × bid_size) / (bid_size + ask_size)
When the bid-side queue is much larger than the ask side, the microprice sits closer to the ask. The intuition: more buyers waiting means the next trade is more likely to lift the ask, so fair value is closer to the ask.
Microprice is a 5-30 second leading indicator of the actual mid moving. Production bots use it as the reference price for take-profit decisions instead of the naive mid.
Imbalance as a directional signal
From production observation: when imbalance flips from -0.3 to +0.5 in 10 seconds without an accompanying news event, the mid moves up by 1-2 cents within the next 30-60 seconds about 65% of the time.
That's a real edge but it dissolves at small position sizes after fees. To monetize, the bot must size enough to capture the move minus fees, but small enough not to move the book itself. Polymarket's books are typically thin enough that anything over 50 shares moves the market.
Combine imbalance with other features: trade velocity (more trades = real signal), best-bid actually moving up (not just depth shifting), market not in news-driven mode.
Signal half-life on Polymarket
The imbalance signal decays. Production data from our trader: imbalance > 0.6 → expected mid move of 1.2c within 60s, half-life of ~30s. After 90 seconds the predictive value has gone to zero.
Implication for bot design: react quickly or skip. A bot that takes 15 seconds to decide is consuming half the edge before placing the order. Latency budget for imbalance strategies should be under 5 seconds from signal to FOK fired.
Strategies that hold positions for longer than the half-life (1-2 minutes) are gambling on the next signal, not the current one. Be explicit about this; don't accidentally hold imbalance-driven positions to resolution.
When imbalance signals lie
The signal misleads when one of three conditions holds.
- News-driven move: the imbalance is a consequence of news that you have not seen. Trading against it loses; trading with it is news arbitrage, a different strategy.
- Whale spoofing: a large order placed and quickly cancelled creates fake imbalance for the duration. Filter by checking that the imbalance persists for 10+ seconds before triggering.
- End-of-period rebalancing: market makers pulling quotes for inventory reasons rather than information reasons. The imbalance reverses minutes later when MM re-quotes.
The combined filter is: imbalance > threshold AND trade velocity > baseline AND no news event in last 5 minutes. Each filter alone has too many false positives.
Code: compute imbalance every WS tick
Reference: subscribe to WebSocket book updates, recompute imbalance on every tick.
def on_book_message(msg):
bids = msg.get("bids", [])[:5]
asks = msg.get("asks", [])[:5]
bid_usd = sum(float(b["price"]) * float(b["size"]) for b in bids)
ask_usd = sum(float(a["price"]) * float(a["size"]) for a in asks)
total = bid_usd + ask_usd
if total < 100: return # illiquid
imb = (bid_usd - ask_usd) / total
state[msg["asset_id"]] = {
"imb": imb,
"best_bid": float(bids[0]["price"]) if bids else 0,
"best_ask": float(asks[0]["price"]) if asks else 1,
"ts": time.time()
}
# decision logic with cooldown + filters
if imb > 0.6 and time.time() - last_fired.get(msg["asset_id"], 0) > 60:
check_filters_and_maybe_fire(msg["asset_id"])
State is per-token. Cooldown prevents over-firing on the same signal. Filters (news check, trade velocity) gate the actual trade.





