> youcanbuildthings.com
tutorials books topics about
tutorial from: Polymarket Profits 2

How to Build a Polymarket Resolution Scanner

by J Cook · 9 min read·

Summary:

  1. Resolution scanning catches the gap between “event resolved” and “price updated”.
  2. Build a scanner that watches 4 event types and rejects stale events after 15 seconds.
  3. Copy-paste an edge calculator that subtracts fees and dispute risk from gross spread.
  4. Fire only on mechanical markets where the answer is public (price levels, CPI releases, Binance candles).

My first version of this scanner bought three markets too late and lost $400 in a single afternoon. The strategy was sound. My news API polled every 60 seconds, and every trade fired after other bots had already moved the price. The fix took one line of code. The working scanner caught the next event at 8 seconds and banked $165 on a single trade.

Resolution scanning is one of the most reliable edges on Polymarket because every market ends at $1.00 or $0.00. The strategy is not arbitrage. It is not momentum. It is just reading public data faster than the order book updates. The window closes as more bots enter, but it is still open on markets the speed bots skip.

What is resolution scanning?

Polymarket resolution scanning edge window chart showing YES share price climbing on an S-curve from $0.55 at event resolution to $1.00 over 60 seconds, with entry markers at 8 seconds (your scanner, around $0.62), 15 seconds (staleness cutoff, around $0.72), 30 seconds (edge gone, around $0.93), and 60 seconds (price hits $1.00) — the profitable window closes within the first 15 seconds

Resolution scanning is a Polymarket strategy that buys shares in a market after the event resolves but before the price moves to $1.00. Every Polymarket market ends the same way: the event resolves and shares pay $1.00 (winners) or $0.00 (losers). The price catches up fast on high-volume markets and slowly on thin ones. Your scanner exploits the slow catch-up.

Here is the math, illustrated on a hypothetical CPI release. “Will CPI come in above 3.0%?” trades YES at $0.55. At 8:30:02 AM the number publishes: 3.2%. Your bot reads the release at 8:30:08. The YES price is still in the low 0.60s because most traders have not reacted yet. Your bot knows the answer is YES, so the shares are worth $1.00 at resolution. You buy at a discount and wait for the price to catch up.

Within seconds the price climbs toward $0.90. Within a minute it is essentially at $1.00 and the edge is gone. Speed is the strategy. The exact cents of edge you capture depend on your latency and the depth of the book at the moment you hit the order.

How do you build a resolution scanner?

You build it in four parts: event detection, market matching, edge calculation, and the staleness filter that protects you from stale events. Start with scheduled announcements (the easiest category) and add market types later.

Step 1: Pick a market type

From Chapter 5 of Polymarket Profits 2, there are four resolution event types and each has different tradeoffs:

Event typeExample marketDetection speedCompetition
Scheduled announcements”Will CPI exceed 3.0%?”SecondsVery high
Price-level triggers”BTC above $100K at 5:00 PM”Sub-secondHigh
Sports / measurable”Will Team X score in Q1?”SecondsMedium
Breaking news”Will X announce Y today?”MinutesLow (messy)

For your first scanner, pick price-level triggers. The answer is mechanical (compare a number to a threshold), the data source is free (Binance or Coinbase public WebSocket), and the competition is lower than scheduled economic data because bots have to parse live candles.

Step 2: Find a live candidate market

You can pull a real live crypto resolution market from gamma-api.polymarket.com/markets. At the time of writing, here is one the scanner would watch:

FieldValue
QuestionWill bitcoin hit $1m before GTA VI?
Slugwill-bitcoin-hit-1m-before-gta-vi-872
Resolution sourceBinance BTCUSDT 1-minute candle “High”
End date2026-07-31
Liquidity$474,365

The market resolves YES the instant a single Binance 1-minute candle shows a $1M+ “High” price. Your scanner watches the Binance WebSocket for BTCUSDT, and the instant a candle high crosses the threshold, you know the market is resolving YES before the Polymarket order book reflects it.

Step 3: Stream live prices from Binance

Binance’s public WebSocket is free and pushes candle updates in real time. No API key, no auth.

import asyncio
import json
import websockets

BINANCE_WS = "wss://stream.binance.com:9443/ws/btcusdt@kline_1m"

async def stream_btc_candles(threshold_usd, on_cross):
    """Watch Binance BTCUSDT 1-minute candles for a high crossing.

    threshold_usd   The price level that triggers a resolution.
    on_cross        Callback fired with the candle data when high >= threshold.
    """
    async with websockets.connect(BINANCE_WS) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            kline = msg.get("k", {})
            high = float(kline.get("h", 0))
            if high >= threshold_usd:
                await on_cross({
                    "timestamp": kline.get("T"),
                    "high": high,
                    "open": float(kline.get("o", 0)),
                    "close": float(kline.get("c", 0)),
                    "is_closed": kline.get("x", False),
                })

This is your data source. It pushes a message every time a candle updates. Your bot reads the message, checks the high, and decides whether to trade.

Step 4: Add the staleness check

This is the one-line fix that saved me from the $400 loss.

import time

def is_stale(event_timestamp_ms, max_age_seconds=15):
    """Reject events older than max_age_seconds.

    event_timestamp_ms   Milliseconds since epoch from the event source.
    Returns (is_stale, age_in_seconds).
    """
    now_ms = time.time() * 1000
    age_s = (now_ms - event_timestamp_ms) / 1000
    return age_s > max_age_seconds, age_s

Fifteen seconds is the default. If the event is older than that when your scanner sees it, skip the trade. The price has already moved and your “edge” is a mirage that will cost you money.

I tested this at 60, 30, and 15 seconds. At 60 seconds, the average entry price was within 3 cents of the final value, which after fees is a guaranteed loss. At 30 seconds, edges showed up only on quiet markets. At 15 seconds, edges were consistent on moderate-to-high volume markets. Under 10 seconds is the sweet spot but requires premium data feeds.

What broke on my first scanner?

My first version used NewsAPI with a 60-second poll. The news came out, my bot detected it 45 to 75 seconds later, and by the time the order hit the matching engine the price had already moved 25 cents. Three trades in a single afternoon, all buying at the wrong end of the move. $400 gone.

The fix was two parts: switch to a streaming data source (the Binance WebSocket above for crypto, Twitter streaming API for news), and add the 15-second staleness filter that rejects events the scanner cannot trade profitably. Same strategy, same markets, profitable inside a week.

How do you calculate the edge before firing?

You subtract the real fee (from Chapter 2’s formula) and a dispute-risk term from the gross spread. Skip either one and you will trade markets that look profitable but hit either a fee wall or a resolution reversal.

def resolution_edge(entry_price, fee_rate_bps=720,
                    dispute_risk_bps=100):
    """Calculate per-share edge for a resolution trade.

    entry_price        The YES price you plan to buy at.
    fee_rate_bps       Market category fee rate in basis points.
    dispute_risk_bps   Heuristic starting default for the probability
                       that a resolution gets disputed and reverses.
                       Polymarket uses an optimistic-oracle proposal
                       and dispute process (see docs.polymarket.com
                       /resolution for the real dispute mechanics).
                       Starting defaults: 100 bps for clean mechanical
                       markets, 300-500 bps for political/policy.
                       Replace these with measured rates from your
                       own history once you have 30+ resolved trades.

    Returns a dict with gross_edge, per_share_fee, per_share_dispute_loss,
    net_edge, return_pct.
    """
    gross_edge = 1.0 - entry_price

    fee_rate = fee_rate_bps / 10000
    per_share_fee = fee_rate * entry_price * (1 - entry_price)

    dispute_rate = dispute_risk_bps / 10000
    per_share_dispute_loss = dispute_rate * entry_price

    net_edge = gross_edge - per_share_fee - per_share_dispute_loss

    return {
        "entry_price": entry_price,
        "gross_edge": gross_edge,
        "per_share_fee": per_share_fee,
        "per_share_dispute_loss": per_share_dispute_loss,
        "net_edge": net_edge,
        "return_pct": net_edge / entry_price * 100 if entry_price else 0,
    }


# Worked example: buy YES at $0.67 on a crypto market
edge = resolution_edge(entry_price=0.67, fee_rate_bps=720)
for k, v in edge.items():
    print(f"{k}: {v}")
# entry_price: 0.67
# gross_edge: 0.33
# per_share_fee: 0.01593 (crypto category)
# per_share_dispute_loss: 0.0067 (1% dispute risk)
# net_edge: 0.30737
# return_pct: 45.87

Net edge of $0.307 per share on 500 shares is $153 from one trade. Compare that to an arbitrage scanner making $2 to $3 per trade. Resolution scanning fires less often but pays more when it hits.

The dispute term matters most on political and policy markets where the resolution source is open to interpretation. Set dispute_risk_bps to 300 for politics, 500 for anything involving judgment calls, and 100 for clean crypto price-level or CPI-style markets.

How fast does the data source actually need to be?

Under 15 seconds for a profitable retail scanner. The fastest bots hit sub-100ms on FOMC announcements and major earnings, and you will not beat them with a Python script from your laptop. Where retail can still win: price-level triggers on thin markets (Binance WebSocket is free and sub-second), lower-profile earnings releases, and markets where the resolution rules need interpretation. Pure algo bots skip those because the math is not mechanical.

Latency by source type:

SourceTypical lagCost
Binance / Coinbase WebSocket< 1 secondFree
Twitter/X streaming API5–15 seconds~$100/mo
Wire service RSS (Reuters, AP)15–30 secondsFree
NewsAPI polling30–90 secondsFree tier
Bloomberg Terminal< 1 second$2,000/mo

The sweet spot for retail is Binance WebSocket for crypto markets plus a cheap news aggregator (like Pipedream or a paid X API tier) for scheduled events. That runs $50 to $200 a month and keeps you under 15 seconds on most events.

What this article doesn’t cover

This is the detection half. To run it against real money you also need:

  • Market rules parsing. Programmatically extract reference source, symbol, timeframe, threshold, and tie-breakers from a market’s description. Covered in Chapter 5.
  • Token ID lookup. Map a market to its YES and NO CLOB token IDs before ordering. Covered in Chapter 2.
  • Order placement. Signed limit/FAK orders with feeRateBps wired in. Covered in Chapter 4.
  • Post-trade logging. The fields you need in your trade log to measure latency, slippage, and dispute outcomes. Covered in Chapter 12.

The Python above is enough to paper-trade the detection logic and measure your real latency. Wiring in execution is the book’s job.

Run the whole scanner through the paper-trading trainer from Chapter 3 of Polymarket Profits 2 before you go live. The trainer feeds live Polymarket data into every strategy with fake USDC, which is exactly what you need here: resolution trades fire rarely, the edge depends on real market conditions, and you cannot simulate the latency race with backtested data.

What should you actually do?

  • Start with Binance WebSocket + BTC price-level markets. Free, sub-second latency, mechanical resolution. The easiest entry point.
  • Implement the 15-second staleness check first. Before edge calc, before order execution, add the is_stale() filter. Most first-version scanners skip it and lose money.
  • Calculate edge with fees AND dispute risk. The resolution_edge() function above handles both. Never use gross spread to size.
  • Skip political and policy markets until you have a track record. Dispute rates on judgment-call resolutions are meaningfully higher than on mechanical ones. Stick with crypto and economic data where the answer is a number.
  • Run the scanner in paper mode for 50 trades. Real markets, fake money, full edge calculations logged. If paper EV is positive after fees + slippage + dispute risk, go live at small size.

bottom_line

  • Resolution scanning pays more per trade than arbitrage but fires less often. The scanner that finds 2 trades a day at $150 each beats one that finds 20 trades at $3 each.
  • Your 60-second news poll is too slow. Switch to streaming or do not run this strategy.
  • The dispute risk term is the difference between a profitable scanner and one that gets wiped out by the one resolution that flips. Always subtract it.

Frequently Asked Questions

What is resolution scanning on Polymarket?+

Resolution scanning detects when a Polymarket event has resolved before the price catches up. A bot reads the public resolution source faster than other traders and buys shares at a discount before the market moves to the final $1.00 payout.

How much edge does a resolution scanner have?+

The edge depends on your detection lag. Observed ranges on mechanical crypto price-level markets: double-digit cents at single-digit seconds of lag, narrowing fast past 30 seconds and essentially gone past 60 seconds. Actual edge varies by market depth and competitor speed.

What data source is fast enough for a resolution scanner?+

Binance and Coinbase public WebSocket feeds for crypto prices (sub-second), wire RSS feeds for scheduled economic data (~15 seconds), and the Twitter/X streaming API for breaking news (~10 seconds). NewsAPI polling is too slow at 30–60 seconds.