The Kelly criterion says to stake the fraction f = (p·D − 1) / (D − 1) of your bankroll on a bet with win probability p and decimal odds D. It maximises long-run growth, but only if p is right, and it is rough even then: in the simulation below, full Kelly with a true 20% edge had a median worst drawdown of 90%. Arbitrage bettors do not need it, because a surebet has no outcome variance to size against.
Below: the derivation, a textbook example I recomputed, kelly() in TypeScript and Python, a Monte Carlo of half, full and double Kelly (fractional Kelly), and 30 days of esports arbitrage data (Sep 6 – Oct 6, 2026). If you only need the numbers for one bet, use the Kelly criterion calculator.
The Kelly formula in three lines
Stake a fraction f of your bankroll at net odds b = D − 1. A win multiplies the bankroll by 1 + f·b, a loss by 1 − f. Kelly maximises the expected log of the bankroll, which is the long-run growth rate:
g(f) = p·ln(1 + f·b) + (1 − p)·ln(1 − f)
g'(f) = p·b / (1 + f·b) − (1 − p) / (1 − f) = 0 → f = p − (1 − p)/b
f* = (p·D − 1) / (D − 1) (edge divided by net odds)
The numerator is the expected profit per unit staked. If p·D ≤ 1 there is no edge and the answer is zero: do not bet. According to Wikipedia's article on the Kelly criterion, the rule was described by John L. Kelly Jr. at Bell Labs in 1956 and was applied to blackjack and the stock market by Edward Thorp.
Recomputing the Haghani & Dewey coin example
Wikipedia summarises a 2016 study by Haghani and Dewey (arXiv:1701.01427). 61 participants got $25 and could bet even money on a coin that lands heads 60% of the time. They had 30 minutes, about 300 bets, and prizes were capped at $250. According to that summary, 28% went bust and the average payout was $91.
The Kelly bet is (0.6·2 − 1) / (2 − 1) = 20%. Wikipedia gives the growth as 2.034% per round. I checked it: g(0.2) = 0.6·ln 1.2 + 0.4·ln 0.8 = 0.020136, which is 2.01% in log terms. And e^0.020136 − 1 = 2.034%. It is the same number in two units: log growth and multiplicative growth. The arithmetic expected gain per bet is 4% (0.2 × 0.2), but compounding makes the long-run rate about half of that. Uncapped, $25 × 1.02034^300 is about $10,500.
The same curve gives the other two points we need. Half Kelly (10% stake) grows the bankroll 1.50% per bet, 75% of the full-Kelly rate for half the stake. Double Kelly (40%) is already negative at −0.24% per bet, and growth crosses zero at a 38.9% stake, about 1.95× Kelly.
kelly() in TypeScript and Python
Both versions take decimal odds. For a prediction-market price (for example 48.8¢), the odds are 1 / price, see the odds converter. If a venue charges a fee, pass the after-fee odds.
/** Full-Kelly stake fraction for a two-outcome bet: win probability p, decimal odds d. */
export function kelly(p: number, d: number): number {
const edge = p * d - 1; // expected profit per unit staked
return edge > 0 ? edge / (d - 1) : 0; // no edge, no bet
}
/** Expected log-growth of the bankroll per bet when staking fraction f. */
export function growth(p: number, d: number, f: number): number {
return p * Math.log(1 + f * (d - 1)) + (1 - p) * Math.log(1 - f);
}
from math import log
def kelly(p, d):
"""Full-Kelly stake fraction for a two-outcome bet: win probability p, decimal odds d."""
edge = p * d - 1 # expected profit per unit staked
return edge / (d - 1) if edge > 0 else 0.0 # no edge, no bet
def growth(p, d, f):
"""Expected log-growth of the bankroll per bet when staking fraction f."""
return p * log(1 + f * (d - 1)) + (1 - p) * log(1 - f)
k = kelly(0.6, 2.0)
print(f"Kelly {k:.3f} g(K) {growth(0.6, 2.0, k):.4%} g(2K) {growth(0.6, 2.0, 2 * k):.4%}")
# Kelly 0.200 g(K) 2.0136% g(2K) -0.2447%
I ran both: they agree to four decimals (0.200, 2.0136%, −0.2447%).
Fractional Kelly in a simulation: half, full and double
Expected growth is an average, but you live through one path. This stdlib-only script plays the same bet 300 times, 20,000 paths per stake size, and reports the final bankroll (start = 1), the share of paths that end below the start, and the median worst drawdown.
import random
import statistics as st
def kelly(p, d):
edge = p * d - 1
return edge / (d - 1) if edge > 0 else 0.0
def simulate(p, d, f, bets=300, paths=20_000, seed=1):
rng = random.Random(seed)
finals, drawdowns = [], []
for _ in range(paths):
bank = peak = 1.0
worst = 0.0
for _ in range(bets):
stake = f * bank
bank += stake * (d - 1) if rng.random() < p else -stake
peak = max(peak, bank)
worst = max(worst, 1 - bank / peak)
finals.append(bank)
drawdowns.append(worst)
return sorted(finals), st.median(drawdowns)
p, d = 0.6, 2.0
k = kelly(p, d)
print(f"{'stake':>9} {'median':>8} {'5th pct':>8} {'95th pct':>9} {'< start':>8} {'worst dd':>9}")
for name, mult in (("0.5K", 0.5), ("1K", 1.0), ("2K", 2.0)):
finals, dd = simulate(p, d, mult * k)
n = len(finals)
print(f"{name:>4} {mult * k:4.0%} {st.median(finals):8,.2f} {finals[n // 20]:8,.2f} "
f"{finals[-n // 20]:9,.0f} {sum(x < 1 for x in finals) / n:8.1%} {dd:9.0%}")
Output of python3 -I, run locally (about 2 seconds):
stake median 5th pct 95th pct < start worst dd
0.5K 10% 91.16 5.49 1,513 0.5% 62%
1K 20% 420.17 1.44 122,659 4.6% 90%
2K 40% 0.48 0.00 68,057 51.9% 100%
Full Kelly's median is more than four times higher than half Kelly's (420× against 91×). It also ends below the start in 4.6% of paths against 0.5%, and its median worst drawdown is 90% against 62%. Double Kelly has a median of 0.48×: half the bankroll is gone in the typical run, and 51.9% of paths end below the start. Yet the 95th percentile is still 68,057×, which is why the average is a poor summary and the median is the number to read. An exact binomial calculation agrees: 0.4%, 4.4% and 52.2% of paths end below the start. The final bankroll depends only on the number of wins, and at the median (180 wins in 300) 1.2^180 × 0.8^120 = 420.17.
Why bettors use fractional Kelly: model risk
The probability p is your own estimate. Say the true chance is 55% but you believe 60% at even odds. The true Kelly stake is 10%; you stake 20%, which is exactly double Kelly for the real edge. Growth per bet falls from +0.501% to −0.014%, so a five-point error removes essentially all of the growth. This is the practical case for staking a fraction, and the Kelly calculator has ½ and ¼ options for it.
It is not a free fix. According to Wikipedia, Paul Samuelson argued in 1963 and 1979 that maximising expected log wealth is only the right goal for someone with log utility, and that the losses along the way can be larger than people will tolerate. Karl Whelan, in lecture 17 of his UCD betting-markets course (Spring 2026), goes further. For utility functions other than log (CRRA with risk aversion γ ≠ 1) the optimal stake is not a fixed fraction of Kelly: the ratio changes with the odds and with your belief. He calls "bet half Kelly if you are twice as risk-averse" an urban legend. Treat ½ Kelly as a rule of thumb, not as an optimum.
With several mutually exclusive outcomes the single-bet formula stops being optimal; in Whelan's simulation 17% of the optimal bets had negative expected value and served as hedges. His blunt conclusion: most bettors have no real edge, and then the best stake is zero.
Why an arbitrageur does not need Kelly
An arb is a set of bets on every outcome where the implied probabilities sum to less than one: S = Σ 1/Di < 1. Stake c/Di of the bankroll on leg i. Whatever happens, one leg wins and returns c, so the bankroll becomes 1 + c·(1 − S) in every outcome. There is no p in that formula, so g(c) = ln(1 + c·(1 − S)) always rises with c, and Kelly's answer is: stake as much as you are allowed. Wikipedia's arbitrage betting entry makes the same point: you do not need to estimate outcome probabilities. Here is a two-leg example, best price per outcome from two different books:
odds = [2.10, 2.04] # best price per outcome, two different books
s = sum(1 / d for d in odds) # < 1 means an arbitrage exists
total = 100.0
stakes = [total * (1 / d) / s for d in odds]
payouts = [x * d for x, d in zip(stakes, odds)]
print(f"sum(1/D) = {s:.4f} ROI = {1 / s - 1:.2%}")
print("stakes ", [round(x, 2) for x in stakes])
print("payouts", [round(x, 2) for x in payouts]) # same in every outcome
sum(1/D) = 0.9664 ROI = 3.48%
stakes [49.28, 50.72]
payouts [103.48, 103.48]
The surebet calculator does this split for up to 10 outcomes, with exchange commission and a fixed leg; the method is in how to calculate a surebet. The question for an arber is therefore not "what fraction", but "what limits my size". In our data (Sep 6 – Oct 6, 2026, about 4.87 million esports arbs across 27 books) the limits are these:
- Time. The median arb lived 2.0 s. 32% were seen for 5 s or more and 4.5% for 60 s or more. Size is bounded by how fast you can place both legs. See how long surebets last.
- Book limits. Sportsbooks can cap stakes and restrict accounts that win through arbs (betting limits, why bookmakers limit accounts). Check the maximum stake before you size up.
- Exchange depth. 15.9% of arbs had a Polymarket or Kalshi leg. In live play, 23.9% of those survived 10 s or more, against 14.4% for sportsbook-only arbs. A longer window helps, but you can only fill what the order book of Polymarket or Kalshi holds at the quoted price. More on this side: prediction markets.
- ROI. The median ROI (the best value reached during an arb's life) was 2.89%. Fully compounding that gives about 2.85% log growth per cycle with zero variance, compared with 2.01% and a 90% drawdown for full Kelly on a 20% edge. This holds only if the whole bankroll fits on every arb, and in practice the three limits above bind much earlier.
One caveat. An arb is riskless only if both legs fill at the quoted prices. If a leg is rejected or its price moves, you hold an open one-sided bet, and that remainder is where sizing discipline matters. Settlement rules also differ between books (a cancelled match, a forfeit), so read them. This is why we show the age of every quote next to the ROI: our reading is that an old quote is the one most likely to have moved already.
Where Kelly still applies: value bets
A value bet is a single bet where you believe the price is wrong. It has outcome variance and an estimated probability, which is exactly the setting Kelly was built for. Use kelly() with a conservative p, a fraction (¼ to ½), and a fixed review of whether your estimates were right. Our reading: if you cannot show that your probabilities beat the closing price over a few hundred bets, the right Kelly stake is zero.
FAQ
What is the Kelly criterion formula for betting?
f = (p·D − 1) / (D − 1), where p is your win probability and D the decimal odds. At p = 60% and D = 2.0 it gives 20% of the bankroll.
What is fractional Kelly and which fraction should I use?
You stake a fixed share of the Kelly amount, usually ½ or ¼. In the simulation above, half Kelly kept 75% of the growth (+1.50% against +2.01% per bet) and cut the share of paths ending below the start from 4.6% to 0.5%. Whelan's lecture shows the fraction is not an optimum for non-log utility, so choose it by drawdown you can accept.
Is Kelly betting safe?
No. Even with a correct probability the median worst drawdown over 300 bets was 90% at full Kelly, and a 5-point error in p at even odds turns full Kelly into double Kelly, with growth near zero.
Do arbitrage bettors use the Kelly criterion?
Not for outcome risk. Stakes are split in proportion to 1/D so every outcome pays the same, and the size is set by book limits, exchange depth and time. In Sep 6 – Oct 6, 2026, 32% of esports arbs stayed open 5 s or more.
Try it
Dozor scans 25 sportsbooks plus Polymarket and Kalshi for esports arbs, with the ROI, the age of every quote and a direct link per leg. Free plan: no card and no time limit, every arb with ROI up to 1% in full and without delay; above that you see the count and the best ROI. Open Dozor or start with the Kelly calculator.
Data from Dozor (Sep 6 – Oct 6, 2026), an esports arbitrage scanner covering 25 sportsbooks plus Polymarket and Kalshi. Dozor is an information tool, not a bookmaker. 18+. Betting involves risk of loss; sportsbooks may restrict arbitrage accounts.


Top comments (0)