Strategy · 8 min read

Kelly criterion for binary prediction markets

Full Kelly formula for binary outcomes, why quarter-Kelly is the practical choice, and a ready-to-use sizing function for Polymarket bots.

The Kelly formula for binary bets

Kelly criterion tells you the optimal fraction of your bankroll to bet when you have an edge, defined as:

f* = (p * b - q) / b

Where:

  • p = your estimated probability of winning
  • q = 1 - p (probability of losing)
  • b = net odds (how much you win per dollar risked)

On Polymarket, b is derived from the token price. If you buy YES at $0.40 and it resolves to $1.00, your net profit per dollar spent is (1 - 0.40) / 0.40 = 1.5. So b = 1.5.

function kellyFraction(
  estimatedProb: number,
  tokenPrice: number,
): number {
  const b = (1 - tokenPrice) / tokenPrice  // net odds
  const p = estimatedProb
  const q = 1 - p
  return (p * b - q) / b
}

// Edge example: you estimate 50% chance, market says 40%
console.log(kellyFraction(0.50, 0.40))  // ~0.167 = 16.7% of bankroll

If the result is negative, Kelly says do not bet -- you have no edge.

The fee adjustment

Polymarket taker fees reduce your net odds. At p=0.40 in a crypto binary, the fee on a $100 bet is roughly $1.30 (see the CLOB fees article for the exact formula). That means your actual payout is $60 - $1.30 = $58.70 on a $40 outlay, so b = 58.70 / 40 = 1.4675 instead of 1.5.

function kellyWithFee(
  estimatedProb: number,
  tokenPrice: number,
  feeRate = 0.25,
  exponent = 2,
): number {
  const grossOdds = (1 - tokenPrice) / tokenPrice
  // fee as fraction of notional: fee / (shares * tokenPrice)
  const feeFrac =
    feeRate * tokenPrice * Math.pow(tokenPrice * (1 - tokenPrice), exponent)
  const netOdds = grossOdds - feeFrac / tokenPrice
  if (netOdds <= 0) return 0
  const p = estimatedProb
  const q = 1 - p
  return Math.max(0, (p * netOdds - q) / netOdds)
}

Why full Kelly is dangerous

Full Kelly maximizes long-run geometric growth, but it assumes your probability estimate is exact. In practice your edge estimate has error, and the formula is very sensitive near the edges. A 5% overestimate of your true win probability can result in Kelly recommending twice as much as it should.

The result is high variance: full Kelly implies roughly 50% drawdowns are expected over a long enough run even when you have a genuine edge.

Quarter-Kelly as the practical default

Most professional sports bettors and quantitative traders use a fraction of Kelly, typically 1/4 to 1/2. Quarter-Kelly (25% of the full Kelly fraction) dramatically reduces variance while giving up surprisingly little long-run growth.

const KELLY_FRACTION = 0.25  // quarter-Kelly

function sizePosition(
  bankroll: number,
  estimatedProb: number,
  tokenPrice: number,
): number {
  const f = kellyWithFee(estimatedProb, tokenPrice)
  const dollarsToRisk = bankroll * f * KELLY_FRACTION
  // Minimum bet guard
  return Math.max(dollarsToRisk, 0)
}

Mapping to shares

On Polymarket you buy shares, not dollars. Convert the dollar size to shares by dividing by the token price.

function dollarsToShares(dollars: number, tokenPrice: number): number {
  const shares = Math.floor(dollars / tokenPrice)
  return Math.max(shares, 0)
}

// Full sizing pipeline
function computeOrderSize(
  bankroll: number,
  estimatedProb: number,
  tokenPrice: number,
): { shares: number; dollars: number } {
  const dollars = sizePosition(bankroll, estimatedProb, tokenPrice)
  const shares = dollarsToShares(dollars, tokenPrice)
  return { shares, dollars: shares * tokenPrice }
}

Edge decay over time

Kelly sizing is only valid for a given edge estimate. If your signal is derived from a short-dated price series (e.g. 60-second Binance momentum), the edge starts to decay as soon as you enter. Bots that hold positions for multiple candles should re-evaluate the implied Kelly fraction each cycle and scale down if the edge is no longer present.

Minimum viable edge

At a 1.5% round-trip fee (entry + exit both as taker), you need an edge of at least 3 percentage points above the market price before Kelly recommends a non-trivial fraction. Below that, quarter-Kelly rounds to zero and the bot correctly skips.

const MIN_EDGE_PP = 0.04  // 4 percentage points

function hasEdge(estimatedProb: number, tokenPrice: number): boolean {
  return estimatedProb - tokenPrice > MIN_EDGE_PP
}

Summary

  • Use the fee-adjusted Kelly formula, not the textbook version.
  • Default to quarter-Kelly (multiply full Kelly by 0.25) to survive estimation error.
  • Convert dollar size to shares using the current best ask.
  • Skip any trade where fee-adjusted Kelly rounds to zero.

Related bots