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 winningq= 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 bankrollIf 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
poly15m-v6 — Enhanced orderbook signal BTC scalper
Polymarket book + Chainlink RTDS + CVD + OFI + chop filter + trained meta-model. Needs 3 of 8 confirmation layers before FOK taker at $0.52. Quarter-Kelly with drawdown scaling, holds to resolution.
polydaily — Daily BTC pair arbitrage bot
Buy-both-sides Gabagool on daily BTC markets (threshold + Up/Down). Picks up cheap YES + NO, locks profit when pair stays below $0.95. Tracks up to 3 markets at once across a ±$1k strike range.