Resolved Markets logoResolved Markets logo
Abstract blue gradient background with a soft light streakAbstract blue gradient background with a soft light streak
Tutorial

Backtest mean reversion on BTC prediction markets

A plain-English walkthrough of testing a mid-price reversion idea on BTC Up/Down markets, the signal, the honest fill model, and the traps that quietly inflate returns.

9 min read · Updated Jun 22, 2026

  • ~20 HzBTC snapshot rate
  • MillisecondsTimestamp precision
  • Full depthBook per snapshot
  • UnlimitedHistory

Mean reversion is the first strategy most quants reach for on a bounded market, and a Polymarket Up/Down contract is bounded by construction: its price lives between 0 and 1. This is a tour of the whole loop, in plain language: the question, the signal, an honest fill model, and the mistakes that make a backtest look better than it is.

The job here is not "read about prediction markets." It is a concrete question: can a short-horizon reversion signal on BTC 5-minute Up/Down contracts survive spread and slippage? We will reason through it end to end, and point you to the exact pages where you can run it yourself.

The three things every backtest needs

Strip a strategy backtest down and it is always the same three pieces. Get any one wrong and the result is fiction.

The right data

A reversion signal needs the price path and the book behind it, not just the last trade.

  • Full bid/ask depth per snapshot
  • Millisecond timestamps
  • Both UP and DOWN tokens

A clear signal

Because the price is already a probability, "distance from the mean" is normalised, no volatility scaling needed.

  • Rolling mean of the mid
  • Enter when stretched below
  • Exit on the return to mean

An honest fill

Fill at the price you would really pay, lift the ask to buy, hit the bid to sell, and charge the spread every time.

  • No mid-price fills
  • Spread on entry and exit
  • Size capped to resting depth

Pulling the data you need

Before any signal, you need a clean price path with the book attached. The snapshot endpoint gives you exactly that: a time-range query against a single market returns ordered records, each carrying best bid, best ask, mid price, spread, and the depth totals on each side, plus the full bids and asks arrays when you ask for the book. Because every record is millisecond-stamped, the series reconstructs the market tick by tick with no interpolation.

  • mid_priceThe series your mean is built on
  • best_bidbest_askThe prices you actually fill at
  • spreadThe cost charged on every round trip
  • bid / ask depthThe cap on how much size is real
  • event vs capture tsWhat was knowable, and when
  • sequence_numberGap detection across the stream

One window of a few thousand snapshots is enough to prototype a single market. Closed markets stay queryable, so you can pull a basket of expired 5-minute contracts and test the same rule across all of them rather than cherry-picking one good run.

Designing the signal

The rule is intentionally simple. Track a rolling mean of the mid price. When the mid drifts a set distance below that mean, the contract is "cheap" relative to its recent self, buy the UP token. When it climbs back to the mean, close. Simplicity is a feature: fewer parameters means fewer ways to accidentally fit noise.

The one decision that matters is the window. A short window reacts fast but whipsaws; a long window is calmer but late. On 5-minute BTC books, where a contract only lives for minutes, the window has to be short enough to act before the market resolves, which is exactly why you want the dense, high-frequency history rather than sparse samples.

The fill model is where backtests lie

The honest part

Charge the spread on every trade

Most prediction-market backtests quietly fill at the mid price, a price nobody can actually trade at. That single shortcut overstates returns by half the spread on every entry and exit. Because each snapshot carries the full bid and ask arrays, you can fill at the touch and watch the spread widen into resolution, exactly as it would against your real order.

What quietly breaks a result

  • Lookahead bias, a snapshot must never trade on information it could not have had yet. Build signals on prior values only.
  • Resolution edge, in the final minutes a contract converges to 0 or 1. Reversion signals fire constantly there and lose. Treat the endgame as its own regime.
  • Survivorship, test across many markets, not one lucky window. Closed markets stay queryable, so include them.
  • Liquidity, a signal that needs more size than the book holds is not tradable. Cap position size to the resting depth you can actually see.

None of these are exotic. They are just the difference between a number you can stake money on and a curve that only works in a slide deck.

Reading the result honestly

A backtest produces an equity curve, but the curve is the least useful thing it gives you. What tells you whether the edge is real is the texture underneath it: where the trades clustered in time, how the win came apart when you charged the full spread, and whether the same rule held on markets it was never tuned on.

  • Trade count, a beautiful curve from nine trades is not evidence. Demand a sample large enough to mean something.
  • Cost sensitivity, re-run with the spread doubled. An edge that only survives at perfect fills is not an edge.
  • Out-of-sample markets, tune on one basket of contracts, confirm on a different basket you never looked at.
  • Drawdown shape, a flat curve that quietly bleeds in the resolution window is hiding the regime that kills it.
A backtest is not a promise of profit, it is the most rigorous way to disprove your own idea before the market does it for you, with money on the line.

Run it on real data

The visual Strategy Builder lets you test this exact idea without writing a fill model by hand, or pull the same snapshots through the API and run it in your own notebook.

Frequently asked questions