BANGER
Product status: Banger’s public beta is paper-only. References to live execution are future-looking. Venue rules and legal claims can change; verify current primary sources for your jurisdiction.
2026-06-11 · 4 min read · reviewed 2026-10-14

Mean-Reversion Strategies for Prediction Markets

Mean reversion is a hypothesis that a measured deviation from a chosen baseline tends to shrink over a specified later window. It needs evidence for the instrument and sample being studied. A contract below its recent average is not automatically cheap, and a stock is not guaranteed to recover either.

An event contract adds a concrete complication: the outcome can resolve before the proposed reversion occurs. For an ordinary binary payout, the losing side can become worthless. Special or scalar payout rules need their own model. The old average is an observation about earlier prices, not a promise about the final outcome.

Choose a baseline that answers a question

A recent average, a reference model, and a related-market price are different baselines. Specify which one you use, how it is constructed, and when its inputs became available. A rolling mean can summarize a window while being a poor estimate of fair value after new information.

Keep the current observation out of the earlier baseline if that is the experiment's definition. Preserve the window length and sampling clock; do not silently change them until the result looks attractive. Distinguish event time from the time your collector actually received the data.

Compute a feature before inventing a signal

The standalone example below uses hypothetical dollar prices. It returns a deviation and, when the history has nonzero variance, a standardized deviation. It places no orders and uses no venue credentials. Its z value is not evidence that prices are normally distributed or that a conventional threshold is profitable.

from decimal import Decimal
from statistics import mean, pstdev


def deviation_observation(history, latest):
    # Price observations in dollars. This computes a feature, not an order.
    values = [Decimal(str(v)) for v in history]
    price = Decimal(str(latest))
    if not all(v.is_finite() and 0 <= v <= 1 for v in values + [price]):
        raise ValueError("prices must be finite and between zero and one")
    if len(values) < 2:
        return None  # Warm-up; the latest observation is excluded from history.
    baseline = mean(values)
    deviation = price - baseline
    sigma = pstdev(values)
    z = deviation / sigma if sigma else None
    return {"baseline": baseline, "deviation": deviation, "z": z}


print(deviation_observation(["0.48", "0.50", "0.52"], "0.44"))
# Hypothetical baseline 0.50 and deviation -0.06.
# A negative deviation does not establish undervaluation or an entry signal.

Insufficient history produces no observation, and a flat history produces no standardized value. Do not replace either condition with a confident trading signal. The code was exercised offline on warm-up, flat, moving and invalid-price fixtures.

Investigate why the price moved

Before treating a deviation as an overreaction, check the contract's exact rules and contemporaneous information. A new official release, a changed outcome expectation or a nearing deadline can explain a durable repricing. The existence of a previous average cannot override that evidence.

An illustrative price drop from 0.55 to 0.30 can either reverse or continue toward a losing settlement. Those are hypothetical paths. Decide in advance which observations would invalidate the hypothesis, how to represent a delayed outcome, and how a desired exit would actually be modeled. A price stop is not a guarantee that an exit executes there.

Costs and fill assumptions are part of the experiment

Kalshi's order book separates bids and asks. A feature calculated from a midpoint is different from buying at an ask and later selling at a bid. Record the intended quantity and available data, not just the plotted price.

Avoid asserting that maker fills always create an edge or that aggressive fills always destroy one. Compare the particular fee and execution assumptions that were tested. A passive fill rate has no universal pass/fail cutoff independent of the strategy, sample and simulator.

Banger's forward-paper implementation in the public 0.1.7 package can assume a fill at a non-crossing requested limit, explicitly logged as a paper simplification. That path cannot validate passive queue position. Keep it distinct from hosted historical backtesting and from any real venue execution.

Carry the calculation into bounded paper research

Banger's real strategy hook is on_market_tick(market, tick, ctx). The SDK declares its RiskEnvelope through the strategy's risk field; fabricated callback names, arbitrary class attributes or a made-up YAML schema do not configure those controls. Consult the public package and CLI documentation when integrating a feature.

Banger's public beta does not accept public live deployments. A forward paper test can help expose implementation and modeling errors; it cannot demonstrate that mean reversion is a reliable edge or make a real-money strategy safe. Treat the result as dated evidence for a specific hypothesis.

Sources

Keep reading

Run your first strategy free

Paper-trade on Polymarket and Kalshi market data without venue keys.

Start free