How We Turn Sports Win-Probability Models Into Polymarket Edge Signals in Real Time

May 11, 2026 · 12 min read

A calibrated win-probability model is the entry ticket to prediction-market trading. Turning it into an actual edge signal that fires real orders against a real market is a separate engineering problem with its own gotchas. This post walks through the live pipeline we run on Polymarket — score polling, fair-probability computation, edge math, freshness gating, and order placement — at the level of detail you would need to build one yourself.

The five-stage pipeline

Every signal that reaches our order placement layer has passed through five stages, in this order:

  1. Live game state ingestion from ESPN, official league APIs, or sport-specific feeds (lolesports, MLB Stats API, NHL API).
  2. Fair probability computation by feeding the game state into the per-sport model.
  3. Live market price ingestion via Polymarket's CLOB WebSocket (orderbook updates push, no polling).
  4. Edge math + freshness gating — compute the gap between fair and ask, reject anything stale or outside the edge band.
  5. Order placement via the Polymarket CLOB REST API with EIP-712 signatures.

Stages 1-3 run continuously as background tasks. Stage 4 runs on a fast inner loop (every 500ms). Stage 5 fires only when stage 4 emits a signal. Total end-to-end latency, from game-state event to order placement, is 800-1500ms in normal operation.

Stage 1: Game state polling cadence

The single most consequential design decision is polling cadence. Too slow and the market beats you to every move. Too fast and you burn rate limit on idle games.

Our cadence varies by sport based on event density:

Within each cadence, we de-duplicate state. If the game state has not changed since the last poll, we do not re-compute the fair probability. This cuts compute cost by roughly 60% on slow-moving games.

Stage 2: Fair probability computation

The fair probability comes from a per-sport pickled model — XGBoost with isotonic calibration for most sports, a Poisson process for soccer (which needs the draw outcome), and a hierarchical point-game-set model for tennis. Each model takes the current game state as input and emits a calibrated win probability for the home side (or, for soccer, three outcomes summing to 1.0).

The compute cost per prediction is small — single-digit milliseconds for XGBoost, slightly more for the soccer Poisson because of the score grid summation. With per-sport caching of the model file in memory, we can produce predictions at well over 100/second per worker.

Stage 3: Polymarket WebSocket subscription

The CLOB WebSocket pushes orderbook updates as they happen. We subscribe to the YES tokens for every active game contract and maintain an in-memory order book. The best ask updates within milliseconds of any market participant's action. This is dramatically lower latency than polling the REST endpoint, which we use only as a fallback when the WebSocket disconnects.

Polymarket's WebSocket is generally reliable but disconnects every few hours during maintenance windows. The handler reconnects automatically and replays any missed orderbook snapshots from REST to bring the in-memory state current. Without this reconnect logic, the bot silently goes blind for hours at a time and you do not notice until you check trade volumes the next morning.

Stage 4: Edge math and freshness gating

The inner loop runs every 500ms. For each watched contract, it checks:

if (now - quote.ts) > MAX_QUOTE_AGE_S: skip
if (now - game_state.ts) > MAX_GAME_AGE_S: skip
fair_prob = predict(game_state)
edge_c = fair_prob * 100 - quote.ask_c - taker_fee_c
if MIN_EDGE_C <= edge_c <= MAX_EDGE_C: emit_signal

The MIN_EDGE_C and MAX_EDGE_C thresholds are sport-specific. The minimum is calibrated to clear costs (typically 5-8 cents). The maximum is the lesson we paid for in tennis — see our edge band post. Without the maximum, the bot fires on its own model errors.

The freshness gates (MAX_QUOTE_AGE_S, MAX_GAME_AGE_S) are 5 and 8 seconds respectively. If either side is stale beyond that, the trade is skipped. Stale data is the worst kind of trade decision because the apparent edge has usually already disappeared.

Stage 5: Order placement

The order is placed via Polymarket's CLOB REST API. The request needs an EIP-712 signature from the trading wallet. We use the proxy-wallet pattern (Gnosis Safe v1.3.0 wrapper) for security — the EOA private key signs, but funds live in the proxy.

Order size is set by quarter-Kelly sizing (see our Kelly post). Order type is FOK (fill-or-kill) at the best ask price. If the order does not fill instantly, we do not chase — the price has already moved and the edge math no longer applies.

Latency from signal emission to order placement is typically 200-400ms. The EIP-712 signing is the slowest step at around 50-100ms; the network round-trip to the CLOB is 100-200ms.

What can go wrong

Every production bot has been bitten by these in some order:

What gets monitored

Three metrics matter more than P&L itself, because they are leading indicators rather than lagging ones:

P&L is the lagging confirmation. CLV is the leading edge signal. Watch both. Trust CLV first.

The bottom line

Building a real-time edge signal pipeline is mostly engineering, not modeling. The model produces the probability. The pipeline gets the probability into a market fast enough, with enough freshness checks, and with enough idempotency to actually be tradable. Most amateur attempts fail at the engineering layer, not the modeling layer.

If you are building one, build the freshness checks and the WebSocket reconnect first. Add the order placement last. The longest-running production bots are the ones with the most paranoid plumbing.

Real-time edge signals across 11 sports

ZenHodl publishes calibrated win probabilities and edge signals for NBA, NHL, MLB, NCAAMB, NCAAWB, CFB, NFL, soccer, tennis, CS2, and LoL. Seven-day free trial.

Try ZenHodl free