All articles
Risk·March 28, 2026·7 min read

Managing UMA Resolution Risk in Polymarket Bots Pre-Settlement

Polymarket resolution risk management for bots: quantify the odds a UMA proposal resolves against your position and auto-exit before the challenge window closes.

Polymarket doesn't settle when the event happens — it settles when a UMA proposer stakes a bond, a two-hour (sometimes longer) challenge window elapses, and no disputer shows up. That gap between "the outcome is obvious to a human" and "the market is finalized on-chain" is where bots lose money they thought was locked in. If your position is on the winning side but a proposer posts the wrong outcome and nobody disputes in time, the UMA Optimistic Oracle will resolve against you, and there is no appeal that runs on your timeline.

This article is about quantifying that specific tail — the probability that a proposal resolves against your open position — and building programmatic exit rules that fire before the window closes rather than after you've been settled into a loss.

Where the risk actually lives

The Optimistic Oracle (OO) is optimistic by design: it assumes proposals are honest unless challenged. On Polymarket's UMA integration, resolution runs through the UmaCtfAdapter contract, which requests a price from the OO, waits for a proposal, and then respects the liveness period. Three things can go wrong for a bot holding shares:

  • A wrong-but-unchallenged proposal. Someone proposes NO on a market that clearly resolved YES, and no disputer catches it before liveness expires. Rare, but it happens on low-volume markets nobody is watching.
  • A legitimately ambiguous question. The market text has a loophole, UMA's DVM (Data Verification Mechanism) votes on the technicality, and the "obvious" outcome loses. This is not a bug; it's the resolution source being more literal than you were.
  • A dispute that flips your expected payout. You were pricing the shares at ~0.97 assuming quick resolution. A dispute drops them to 0.60 while the DVM vote runs for two-plus days, and your mark-to-market cratered even if you're eventually right.

The first two are outcome risk. The third is duration and mark risk, and it's the one most bots ignore because they model resolution as instantaneous.

Quantifying the tail

You can't get UMA to tell you the true probability of an adverse resolution, but you can build a usable proxy. The inputs I actually track per market:

  • Proposer identity and history. The OO emits a ProposePrice event with the proposer address. Keep a rolling table of proposer addresses and their dispute/overturn rate. A first-seen address proposing on a thin market is a different risk than a known market-maker who has proposed 4,000 times cleanly.
  • Bond size relative to market notional. A small bond on a large market means disputing is cheap relative to the prize — good, disputers are incentivized. A large bond on a tiny market means nobody will bother, and a wrong proposal can sail through.
  • Time-in-window remaining. Liveness minus (now − proposal timestamp). This is your hard deadline.
  • Question ambiguity score. A cheap NLP pass over the market rules text flagging conditional clauses, "at the time of," specific timezones, and resolution-source dependencies. Markets that reference an external source that could be down or revised are the ones that bite.

Fold these into a single adverse-resolution probability p_adv. It doesn't need to be calibrated to three decimals; it needs to be good enough to rank markets and trip a threshold. The same discipline of turning fuzzy signals into a single actionable number is what we cover in our writeup on position sizing on Hyperliquid with Kelly and vol targeting — the estimator is rough, but the decision boundary it feeds is precise.

The exit rule that matters

Here's the core logic. You hold shares priced near 1.0 pending resolution. Your expected loss from holding to settlement is roughly p_adv * position_notional. Your cost to exit now is the spread you pay to dump shares back into the order book at, say, 0.96 instead of the theoretical 0.99. Exit when the expected adverse loss exceeds the round-trip exit cost, with a hard override that forces an exit once you cross a time-in-window floor.

def should_exit(pos, market, oo_event, now):
    liveness_end = oo_event.propose_ts + market.liveness_secs
    secs_left = liveness_end - now

    # Hard floor: never let the window close while we're deliberating.
    if secs_left < HARD_EXIT_FLOOR:          # e.g. 900s
        return True, "time-floor"

    p_adv = adverse_prob(market, oo_event)    # 0..1
    hold_cost = p_adv * pos.notional
    exit_cost = pos.notional * (pos.mark - best_bid(market))

    # Dispute already live => DVM vote, days of duration risk.
    if oo_event.disputed:
        return True, "disputed"

    if hold_cost > exit_cost * EXIT_MARGIN:   # margin ~1.3
        return True, "ev-negative-hold"
    return False, None

Two things engineers get wrong here. First, best_bid is not static — when a proposal looks shaky, liquidity vanishes on the side you need to sell, so pull the live top-of-book, not a cached quote, and size against real depth. Second, HARD_EXIT_FLOOR should account for your own execution latency plus block inclusion time on Polygon. If your exit needs three blocks to land and you set the floor at 30 seconds, you'll miss it under congestion. I run 15 minutes as a floor and tighten only after backtesting against real liveness periods.

Watching the oracle, not the market

Most Polymarket bots poll the CLOB API and never touch the oracle. That's backwards for resolution risk. Subscribe to the OO contract's ProposePrice, DisputePrice, and Settle events over a websocket to your own Polygon node or a provider like Alchemy. The moment a ProposePrice fires for a market you're in, start the clock and evaluate should_exit on every new block until the window closes or you flatten.

A stale or lagging event feed will silently break this — you think you have 40 minutes of window left when you have four. That failure mode is exactly the oracle-staleness class we describe in building a Solana circuit breaker for depeg and stale-oracle halts; the same halt-on-staleness reflex belongs in any resolution-sensitive bot. Cross-check your node's head block timestamp against wall-clock, and if the drift exceeds a few blocks, treat the feed as untrusted and force-flatten rather than trusting a countdown you can't verify.

A quick worked example

You hold 20,000 shares of a YES outcome marked at 0.98, notional ≈ $19,600. A proposal comes in for NO. Your p_adv model reads: proposer is a first-seen address (bad), bond is large relative to a $40k market (bad, disputers won't bother), ambiguity score is high because the rules reference a timezone cutoff. Call it p_adv = 0.15.

Expected hold loss: 0.15 * 19,600 ≈ $2,940. Best bid on YES has dropped to 0.90 as others react, so exit cost is 20,000 * (0.98 − 0.90) = $1,600. Hold cost beats exit cost by nearly 2x — you sell into the 0.90 bid, eat the $1,600, and walk away. If you'd waited and the wrong NO proposal settled unchallenged, you'd have lost the full $19,600.

That single decision — take a known $1,600 hit to avoid a 15%-weighted $19,600 loss — is the entire game.

Operational hygiene

  • Whitelist resolution sources. Maintain a set of markets whose resolution sources you actually trust, and size larger only there. Everything else gets a smaller cap and a more aggressive exit floor.
  • Simulate disputes offline. Before you deploy any change to the exit logic, replay historical ProposePrice/DisputePrice sequences and confirm your rule would have flattened in time. Getting a second set of eyes on that path is worth it; a code review and audit of the oracle-watcher and the exit executor catches the race conditions that only show up under Polygon congestion.
  • Alert on window entry, not just exit. The worst outcome is a bot that quietly holds through a window because a websocket dropped. Fire a human alert the instant you enter any liveness window with size on, and surface time-remaining on a live trading dashboard so you can eyeball what the bot is deciding.

Slippage on the exit leg deserves the same care as any other execution — the sell into a thinning book is a price-impact problem, and the guardrails from our Jupiter swap slippage and price-impact writeup port cleanly to Polymarket's CLOB.

None of this is exotic. It's event subscription, a rough probability, one threshold, and a hard time floor — but the teams that skip it are the ones surprised by a settlement they could see coming for two hours. If you want a second engineer to pressure-test your resolution-risk path before it costs you a full position, our ongoing support and maintenance work is built for exactly that kind of standing watch.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#Polymarket#UMA Oracle#Risk Management#Trading Bots