Code Walkthrough · Risk Management · Wave 1

Position Sizing in Python: Kelly Criterion + Fixed-Fractional

Your signal quality is half the game. The other half is how much capital you risk when the signal fires. A positive-expectancy strategy can still blow up an account if position sizing is wrong — and a mediocre strategy can survive if sizing is conservative enough. This walkthrough implements Kelly Criterion and fixed-fractional sizing in ~25 lines of numpy and pandas, using the same worked example as the Kelly Criterion guide so the two pages agree on every number.

Versions: numpy 2.4.6 · pandas 2.3.3 · run 2026-06-05
Inputs: pure math — no market data download required

← Code Walkthroughs · First Trading Bot · Fetch Data · SMA Backtest

position_sizing.py
"""AlgoDrill — Kelly fraction and fixed-fractional position sizing.

~25 lines of numpy/pandas to compute full Kelly, half-Kelly,
quarter-Kelly, and fixed-fractional risk from a trade series.
Pure math — no broker, no execution.

Versions: numpy 2.4.6 · pandas 2.3.3 (run 2026-06-05)
Install:  pip install numpy pandas
"""
import numpy as np
import pandas as pd


def kelly_fraction(win_rate: float, avg_win: float, avg_loss: float) -> dict:
    """Kelly fraction from win rate and average payoffs."""
    loss_rate  = 1.0 - win_rate
    expectancy = win_rate * avg_win - loss_rate * avg_loss  # E[trade]
    kelly_f    = expectancy / avg_win                       # f* = E/W
    return {
        "expectancy":   round(expectancy, 4),
        "payoff_ratio": round(avg_win / avg_loss, 4),
        "kelly_f":      round(kelly_f, 4),
        "half_kelly":   round(kelly_f / 2, 4),
        "qtr_kelly":    round(kelly_f / 4, 4),
    }


# ── Example: 55% WR, $150 avg win, $100 avg loss (matches /kelly-criterion) ──
r = kelly_fraction(0.55, 150.0, 100.0)
print("=== Kelly sizing: 55% WR, $150 avg win, $100 avg loss ===")
print(f"Expectancy      ${r['expectancy']:.2f} per trade")
print(f"Payoff ratio      {r['payoff_ratio']:.4f}")
print(f"Full Kelly        {r['kelly_f']:.1%}")
print(f"Half-Kelly        {r['half_kelly']:.1%}")
print(f"Quarter-Kelly     {r['qtr_kelly']:.1%}")
print()

# ── Fixed-fractional comparison on a $10,000 account ─────────────────────────
ACCOUNT = 10_000
print(f"=== Fixed-fractional risk on ${ACCOUNT:,} account ===")
for pct in (0.01, 0.02, r["half_kelly"], r["kelly_f"]):
    risk = ACCOUNT * pct
    print(f"  {pct:.1%}  → ${risk:>7.2f} at risk per trade")
print()

# ── Batch Kelly from a real trade P&L series (pandas path) ───────────────────
pnl = pd.Series([150, -100, 200, -90, 175, -110, 300, -95, 225, -100])
wins   = pnl[pnl > 0]
losses = pnl[pnl < 0]
wr     = len(wins) / len(pnl)
r2 = kelly_fraction(wr, float(wins.mean()), float(abs(losses.mean())))
print(f"=== Batch Kelly: 10-trade P&L series ===")
print(f"Trades {len(pnl)}  WR {wr:.0%}  Avg win ${wins.mean():.2f}  Avg loss ${abs(losses.mean()):.2f}")
print(f"Full Kelly        {r2['kelly_f']:.1%}")
print(f"Half-Kelly        {r2['half_kelly']:.1%}")

Script Output

=== Kelly sizing: 55% WR, $150 avg win, $100 avg loss ===
Expectancy      $37.50 per trade
Payoff ratio      1.5000
Full Kelly        25.0%
Half-Kelly        12.5%
Quarter-Kelly     6.2%

=== Fixed-fractional risk on $10,000 account ===
  1.0%  -> $  100.00 at risk per trade
  2.0%  -> $  200.00 at risk per trade
  12.5%  -> $1250.00 at risk per trade
  25.0%  -> $2500.00 at risk per trade

=== Batch Kelly: 10-trade P&L series ===
Trades 10  WR 50%  Avg win $210.00  Avg loss $99.00
Full Kelly        26.4%
Half-Kelly        13.2%

Line-by-Line Walkthrough

Lines 14–24: The kelly_fraction function

The function takes three inputs: win_rate (fraction of trades that win, e.g., 0.55), avg_win (average dollar profit on winning trades), and avg_loss (average dollar loss magnitude, positive). It computes:

Lines 27–36: Reading the sizing output

For the worked example (55% WR, $150/$100):

Lines 39–44: Fixed-fractional comparison

Fixed-fractional sizing risks a flat percentage of account equity regardless of Kelly. For a $10,000 account:

Lines 47–54: Batch Kelly from a P&L series

The batch section demonstrates how to compute Kelly from a raw list of trade P&L values using pandas. Separate wins (positive P&L) from losses (negative P&L), compute win rate, average win, and average loss magnitude, then apply the formula. For the 10-trade series [150, −100, 200, −90, 175, −110, 300, −95, 225, −100]:

At only 10 trades, these estimates carry wide confidence intervals. The true win rate for a 50% observed rate at n=10 has a 95% confidence interval of approximately 19–81%. Use Kelly at this sample size as a rough directional check, not a precise sizing target.

When to Use Each Approach

Sizing rule Best when Risk
Fixed 1–2% Starting out, edge unverified, <50 trades of history Grows slowly; leaves Kelly value on the table
Quarter-Kelly Edge verified out-of-sample but still building confidence Still conservative; minimal ruin risk
Half-Kelly 100+ trades of OOS history, stable edge estimates, clear regime Drawdowns are real; requires discipline to hold through them
Full Kelly Theoretically optimal; rarely used live due to drawdown severity 30–50% drawdowns typical; estimation errors are catastrophic

Frequently Asked Questions

What is the Kelly Criterion formula in Python?
The Kelly fraction for a binary win/loss system is: f* = expectancy / avg_win, where expectancy = win_rate * avg_win - loss_rate * avg_loss. In Python: loss_rate = 1.0 - win_rate; expectancy = win_rate * avg_win - loss_rate * avg_loss; kelly_f = expectancy / avg_win. For a system with 55% win rate, $150 average win, and $100 average loss: expectancy = 0.55 * 150 - 0.45 * 100 = $37.50 per trade; kelly_f = 37.50 / 150 = 25.0%. A positive Kelly means the system has positive expectancy. A zero or negative Kelly means stop trading the setup — no position sizing formula fixes a losing strategy.
Why use half-Kelly instead of full Kelly?
Full Kelly maximizes long-run geometric growth but produces extreme drawdowns — often 30-50% or more. Most traders and funds cannot sustain those drawdowns operationally or psychologically, and deviate from the system before the edge compounds. Half-Kelly captures roughly 75% of full Kelly's long-run growth while cutting drawdowns approximately in half. The additional reason is estimation error: win rate and average payoff computed from a limited trade history are noisy estimates. If your true win rate is 52% but you estimated 55%, full Kelly is 1.5x the correct fraction — in the dangerous overbetting zone. Half-Kelly provides a buffer against estimation error. Quarter-Kelly is appropriate when the trade history is small (fewer than 50 trades) or the parameter uncertainty is high.
What is fixed-fractional position sizing?
Fixed-fractional sizing means risking a constant percentage of account equity on each trade — for example, 1% or 2% of current equity per trade. It is simpler than Kelly, does not require estimating win rate and average payoff, and scales automatically with account growth (risk 1% of $10,000 = $100; risk 1% of $20,000 = $200). The drawback is that 1-2% is often well below the Kelly-optimal fraction for a genuinely edge-positive system, so it grows more slowly. Most retail systematic traders start with fixed-fractional sizing (1-2%) and use Kelly as a sanity check — if Kelly says 25% and you are risking 2%, the system has a large margin of safety; if Kelly says 2% and you are risking 2%, you are already at full Kelly and should halve your risk.
How do I compute Kelly from a list of trade P&L values?
Separate wins and losses from your P&L series, compute win rate, average win, and average loss, then apply the Kelly formula. In Python with pandas: pnl = pd.Series([your trade list]); wins = pnl[pnl > 0]; losses = pnl[pnl < 0]; wr = len(wins) / len(pnl); kelly_f = (wr * wins.mean() - (1-wr) * abs(losses.mean())) / wins.mean(). The walkthrough script shows this pattern on a 10-trade series (50% WR, $210 avg win, $99 avg loss) producing a Full Kelly of 26.4%. At fewer than 30-50 trades, the confidence interval on win rate is wide enough that you should treat Kelly as directional guidance rather than a precise sizing target.

The Kelly Criterion guide covers the derivation, the relationship to Sharpe Ratio, and failure modes like multi-position correlation.

Full Kelly Criterion Guide →   All Code Walkthroughs →