trading4 Min Read

Position Sizing & the Kelly Criterion in Python

Gorav Singal

October 30, 2026

TL;DR

Position sizing — how much of your capital to risk per trade — decides survival more than signal quality does. The KELLY CRITERION gives the growth-optimal bet size: for an even-money edge, f* = p − q (your win probability minus loss probability). A Python simulation makes the shape clear: growth peaks AT Kelly, and betting MORE than Kelly is strictly worse — you grow slower AND risk more. Measured over 1000 bets with a 52% edge (Kelly = 4% of capital): half-Kelly grew median wealth x1.82, Kelly x2.23, but 2.5x over-betting SHRANK it to x0.36 (64% of paths ended below start) and 6x over wiped out 100% of paths. Even AT Kelly the drawdown was brutal (−68%), which is why practitioners bet HALF-Kelly: ~75% of the growth for far smaller swings. Sizing, not signals, is what keeps you in the game.

Position Sizing & the Kelly Criterion in Python

Every strategy article so far asked ”what should I trade?” This one asks the question that actually decides whether you survive: ”how much should I bet?” It is the most underrated skill in trading. You can have a genuine edge and still go broke by sizing wrong — and you can have a mediocre edge and compound it safely for years by sizing right. Position sizing is where the money is really made and lost.

A note up front: educational material, not financial advice. Nothing here is a recommendation to trade.

Why sizing dominates

Here’s the uncomfortable truth: a positive edge does not guarantee you make money, because drawdowns compound against you. Lose 50% and you need +100% just to get back to even. Bet too much per trade and a normal losing streak — which will happen even with a good edge — drops you so far that you can’t recover. The question “what fraction of my capital do I risk?” is therefore not a detail; it’s the difference between compounding and ruin.

The Kelly criterion answers it precisely: it gives the bet size that maximises long-run growth. For a simple even-money bet (win +1 or lose −1), with win probability p:

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

So a 52% edge implies f* = 0.04 — risk 4% of your capital per bet. More generally, Kelly scales with your edge and inversely with the odds/variance.

Watch what happens when you vary the bet size

The theory is one thing; seeing the curve is what makes it stick. Let’s simulate many paths at different bet fractions.

Position sizing and the Kelly curve: growth vs bet size is an inverted U peaking at the Kelly fraction. Betting less grows slower but safely; betting more than Kelly grows slower AND riskier, with ruin climbing toward certainty. A table shows median wealth and ruin probability for half-Kelly, Kelly, and over-betting.

import numpy as np

p = 0.52                       # a 52% edge, even money
kelly = p - (1 - p)            # = 0.04

def simulate(f, nbets=1000, npaths=3000, seed=0):
    rng = np.random.default_rng(seed)
    wealth = np.ones(npaths)
    peak = np.ones(npaths); maxdd = np.zeros(npaths)
    for _ in range(nbets):
        outcome = np.where(rng.random(npaths) < p, 1.0, -1.0)
        wealth = np.maximum(wealth * (1 + f * outcome), 1e-9)
        peak = np.maximum(peak, wealth)
        maxdd = np.minimum(maxdd, wealth / peak - 1)
    return np.median(wealth), (wealth < 1).mean(), np.median(maxdd)

for f, label in [(0.02, "half-Kelly"), (0.04, "Kelly"),
                 (0.10, "2.5x over"), (0.25, "6x over")]:
    med, ruin, dd = simulate(f)
    print(f"{label:11} (f={f}): median x{med:5.2f}  P(end<start) {ruin*100:3.0f}%  maxDD {dd*100:4.0f}%")

The measured result: over-betting is ruin

bet size median wealth P(end < start) median max drawdown
half-Kelly (f=0.02) x1.82 20% −41%
Kelly (f=0.04) x2.23 29% −68%
2.5× over (f=0.10) x0.36 64% −98%
6× over (f=0.25) x0.00 100% −100%

Read that table slowly, because it contains the whole lesson:

  • Growth peaks at Kelly (x2.23). This is the most you can expect to compound with this edge.
  • Betting more than Kelly is strictly worse. It’s not “more risk for more return” — past Kelly you get less growth and more risk. At 2.5× Kelly the median path lost money (x0.36) despite a real positive edge. At 6× Kelly, 100% of paths ended below where they started — certain ruin, with a genuine edge, purely from over-betting.
  • Even at Kelly the ride is brutal — a −68% median drawdown. Almost nobody can hold a position through a 68% drawdown without panicking out at the bottom.

Why professionals bet half-Kelly

That last point is why full Kelly is almost never used in practice. Look at half-Kelly: it captures x1.82 of the x2.23 full-Kelly growth — about 75% of the compounding — but with a far gentler −41% drawdown instead of −68%, and a lower chance of ending underwater. Giving up a quarter of your theoretical growth to roughly halve your drawdown is a trade almost everyone should take, because the drawdown you can actually survive (emotionally and financially) is the real constraint. Most serious traders bet half-Kelly or less — and they size conservatively precisely because their estimate of the edge (p) is itself uncertain, and over-estimating your edge pushes you past the ruinous side of the curve.

Practical sizing methods you’ll meet are mostly tamer cousins of this idea:

  • Fixed-fractional — risk a fixed small % (often 0.5–2%) of current capital per trade. Simple, robust, and naturally scales down after losses.
  • Volatility targeting — size positions so each contributes similar risk (bet less on volatile assets, more on calm ones), keeping portfolio volatility steady.
  • Hard caps — a max position size and a max portfolio drawdown “kill switch,” regardless of what any formula says.

Takeaways

  • How much to bet matters more than what to bet on. A real edge sized wrong still goes to zero, because drawdowns compound against you.
  • The Kelly criterion gives the growth-optimal size (f* = p − q for even money). Growth peaks at Kelly.
  • Betting past Kelly is strictly worse — less growth and more ruin. Measured: 2.5× over → median x0.36; 6× over → 100% of paths ruined, all with a positive edge.
  • Even at Kelly the drawdown (−68%) is unbearable, so pros bet half-Kelly (~75% of the growth, far smaller swings) — and smaller still, because the edge itself is uncertain. Sizing keeps you in the game.

Last in the series: a genuine HFT signal — order-book imbalance — and an honest look at why it works and why it’s so hard to actually harvest. For money habits and the psychology that wrecks good systems, see the costs & psychology lesson.

Share

Related Posts

Momentum & Trend Following in Python

Momentum & Trend Following in Python

Momentum is, along with value, one of the most robust and widely-documented…

Mean Reversion & Pairs Trading in Python

Mean Reversion & Pairs Trading in Python

The moving-average crossover bet on direction and lost. Here’s a strategy with a…

Market Making Basics: Spread Capture vs Inventory Risk (in Python)

Market Making Basics: Spread Capture vs Inventory Risk (in Python)

So far you’ve been a taker — crossing the spread to get filled. A market maker…

Order-Book Imbalance: A Microstructure Signal (in Python)

Order-Book Imbalance: A Microstructure Signal (in Python)

The strategies so far used daily or minute bars. This last one goes down to the…

Execution Algorithms: VWAP & TWAP in Python

Execution Algorithms: VWAP & TWAP in Python

Every strategy so far produced signals — when to be long, short, or flat. But a…

The Moving-Average Crossover, Backtested Honestly in Python

The Moving-Average Crossover, Backtested Honestly in Python

If algo trading has a “hello world,” it’s the moving-average crossover. Every…

Latest Posts

Execution Algorithms: VWAP & TWAP in Python

Execution Algorithms: VWAP & TWAP in Python

Every strategy so far produced signals — when to be long, short, or flat. But a…

Momentum & Trend Following in Python

Momentum & Trend Following in Python

Momentum is, along with value, one of the most robust and widely-documented…

Mean Reversion & Pairs Trading in Python

Mean Reversion & Pairs Trading in Python

The moving-average crossover bet on direction and lost. Here’s a strategy with a…

The Moving-Average Crossover, Backtested Honestly in Python

The Moving-Average Crossover, Backtested Honestly in Python

If algo trading has a “hello world,” it’s the moving-average crossover. Every…

Market Making Basics: Spread Capture vs Inventory Risk (in Python)

Market Making Basics: Spread Capture vs Inventory Risk (in Python)

So far you’ve been a taker — crossing the spread to get filled. A market maker…

Order-Book Imbalance: A Microstructure Signal (in Python)

Order-Book Imbalance: A Microstructure Signal (in Python)

The strategies so far used daily or minute bars. This last one goes down to the…