The Kelly Criterion for Prediction Market Sizing

April 21, 2026 · 11 min read

You found a 5-cent edge on a Polymarket contract. How many shares do you buy? A traditional trader's answer might be "2% of capital" or "$500, that's my standard size." Those answers waste your edge. The Kelly Criterion is the mathematically correct answer to exactly this question — and on prediction markets, it matters more than on almost any other trading venue.

This post derives the Kelly formula for prediction market contracts, walks through why full Kelly is rarely the right choice, and shows how to apply fractional Kelly to an automated trading bot with working Python.

What the Kelly Criterion Actually Does

The Kelly Criterion tells you the fraction of your bankroll to stake on a single bet to maximize the long-term geometric growth rate of your bankroll. Two critical words:

The insight: if your edge is small, you want to bet small. If your edge is large, you want to bet bigger — but never so big that a single loss destroys your bankroll. Kelly finds the sweet spot.

The Formula for Binary Contracts

Polymarket contracts are binary: they settle at $1 (your side wins) or $0 (your side loses). If you buy at price p and your estimated true probability is q, the Kelly fraction is:

f* = (q − p) / (1 − p)

Where:

In words: Kelly fraction equals your edge (q − p) divided by the payoff (1 − p) if you win.

Worked example

Polymarket has a contract trading at $0.60 for "Home team wins." Your calibrated model estimates the home team's true probability at 0.72. You have a $5,000 bankroll.

That's a big stake. Which is why almost nobody actually uses full Kelly.

Why Full Kelly Is Too Aggressive

Full Kelly assumes you know q exactly. In practice, you have an estimate of q with some error. Even a calibrated model with Expected Calibration Error of 2% has real uncertainty on any single prediction. Small errors in q produce large errors in Kelly fraction.

Consider the previous example with slightly worse inputs:

Your estimate (q)Market (p)Full Kelly f*Stake (of $5k)
0.720.6030.0%$1,500
0.680.6020.0%$1,000
0.640.6010.0%$500
0.600.600.0%$0
0.560.60−10.0%sell/skip

A 4pp error in your probability estimate moves Kelly by 10pp of bankroll. If your model is even slightly miscalibrated, full Kelly can double the stake you should actually take.

The key insight: you are sizing based on an estimate of your edge, not the edge itself. Shrink accordingly.

Fractional Kelly: The Practical Answer

The industry standard is fractional Kelly: stake a constant fraction of what full Kelly suggests. Common fractions:

FractionNameUse case
1.00Full KellyTheoretical only. Not recommended.
0.50Half KellyExperienced traders with well-calibrated edge estimates.
0.25Quarter KellyStandard starting point for automated bots. Industry default.
0.10Tenth KellyConservative. Early-stage model with uncertain calibration.

Why fractions work: halving the Kelly fraction cuts your growth rate by only ~25% but halves your variance. On a real-world estimator with calibration error, the actual growth rate with half Kelly often exceeds full Kelly because you don't over-stake when q is overestimated.

Our own moneyline trading bot defaults to quarter Kelly with a hard cap at 5% of bankroll per trade. In six months of live data, this produces consistent positive growth rates without the 40-70% drawdowns full Kelly would induce.

Python: Fractional Kelly Sizing

def kelly_fraction(market_price: float, fair_prob: float) -> float:
    """Compute Kelly fraction for a binary contract.

    Returns 0.0 if no edge (or negative edge — caller should sell/skip).
    """
    if market_price >= 1.0 or market_price <= 0:
        return 0.0
    if fair_prob <= market_price:
        return 0.0
    return (fair_prob - market_price) / (1.0 - market_price)

def size_position(bankroll_usd: float,
                  market_price: float,
                  fair_prob: float,
                  kelly_fraction_mult: float = 0.25,
                  max_per_trade_pct: float = 0.05,
                  min_per_trade_usd: float = 1.0) -> float:
    """Returns USD to stake, respecting fractional Kelly + hard cap + min size."""
    f_star = kelly_fraction(market_price, fair_prob)
    if f_star <= 0:
        return 0.0
    stake_pct = min(f_star * kelly_fraction_mult, max_per_trade_pct)
    stake_usd = bankroll_usd * stake_pct
    if stake_usd < min_per_trade_usd:
        return 0.0
    return stake_usd

# Example
stake = size_position(
    bankroll_usd=5000,
    market_price=0.60,    # market ask = 60c
    fair_prob=0.72,       # model fair = 72%
    kelly_fraction_mult=0.25,  # quarter Kelly
    max_per_trade_pct=0.05,    # hard cap 5%
)
# stake = $250 (quarter Kelly suggests 7.5%, capped at 5%)

Adjustments for Real Markets

Execution cost

Kelly assumes you get the quoted price. In reality, you'll pay slippage, taker fees, and lose to latency. Shrink your fair_prob by a small buffer (0.005-0.01) before computing Kelly. This correctly models "the edge I capture is smaller than the edge I calculate."

Correlation across positions

Kelly assumes your bets are independent. On prediction markets, they rarely are: two positions on different NBA games in the same slate have correlated model risk (if your NBA model is miscalibrated, both trades lose). Never let Kelly put more than ~15% of bankroll across all same-sport simultaneous positions, regardless of individual Kelly signals.

Maximum number of concurrent positions

If you have 20 open positions each sized at 2% Kelly, that's 40% of bankroll at risk simultaneously. Limit either per-position size or total concurrent exposure. A practical rule: max_concurrent_exposure_pct = 15% to 20%.

Skip the sizing math. ZenHodl's API delivers calibrated fair probabilities for 11 sports, and the included bot course shows how to size trades with Kelly-based risk management.

See ZenHodl

When Kelly Is the Wrong Tool

Summary

Further reading: Understanding Edge in Prediction Markets · Risk Management for Automated Bots · Reading Polymarket Order Books