In 2006, I started trading professionally. As I was walking in on my first day, someone was walking out for their last. A week later, two more people left their screens for the last time. The following week? another one.
It was brutal.
Later that month, the managing director held an all-hands and told us something I’ll never forget. He said there’s one thing that ruins traders’ careers faster than anything else:
Trading too big.
Since that day, I’ve been obsessed with keeping my risk under control. And position sizing is one of the most important aspects of doing it.
Unfortunately for most people, risk management—and position sizing—is an afterthought.
And usually only after their account gets to $0.
In today’s newsletter, you will find the optimal position size for the S&P500 using the Kelly criterion.
The Kelly criterion is a formula that determines the optimal size for a bet. It’s the position size that maximizes the expected growth rate of an account. John Kelly invented it in the 1950s and used it for gambling. In the 1960s, Edward Thorp popularized it in two famous books (Beat the Dealer and Beat the Market).
It was said that Warren Buffet and Bill Gross use versions of the Kelly criteria, too.
The Kelly criterion is valid when expected returns are known (it maximizes the expected growth rate). It also assumes you can make infinite bets. Most people can’t predict the returns of the stock market—or make infinite bets—and this can lead to ruin.
Which is a frequent (and justified) criticism.
In successful trading systems, however, you can estimate your expected returns by past system returns. If your trading system wins 60% of the time over hundreds (or thousands) of trades, there’s a good chance you’ll continue to win.
To show how you can use the Kelly criteria, you’ll find the optimal position size for the S&P500 index.
Imports and set up
Import the OpenBB SDK for data, NumPy for math, and SciPy for optimization.
%matplotlib inline from openbb_terminal.sdk import openbb import numpy as np from scipy.optimize import minimize_scalar from scipy.integrate import quad from scipy.stats import norm
Next, grab data for the S&P500 index. You can use portfolio returns, too.
annual_returns = ( openbb.economy.index( ["^GSPC"], start_date="1950-01-01", column="Close" ) .resample("A") .last() .pct_change() .dropna() ) return_params = ( annual_returns["^GSPC"] .rolling(25) .agg(["mean", "std"]) .dropna() )
This grabs daily price data and resamples it to annual data taking the last value of the year. Then it computes the annual return. To estimate expected returns, take the mean and standard deviation.
Compute the optimal position size
You only need two functions to get the optimal position size with the Kelly criterion.
Kelly defines the growth rate as the integral of the probability distribution of returns. The first function defines it.
def norm_integral(f, mean, std): val, er = quad( lambda s: np.log(1 + f * s) * norm.pdf(s, mean, std), mean - 3 * std, mean + 3 * std, ) return -val
This code integrates the function that Kelly defined as the probability distribution of returns. The function is integrated between -3 and 3 standard deviations. (The integral is the area under a function between two points.) Note this function returns the negative integral. That way you can optimize it by minimization in the next step.
Next, solve for the optimal value that maximizes the expected return using SciPy (by minimizing the negative integral).
def get_kelly(data): solution = minimize_scalar( norm_integral, args=(data["mean"], data["std"]), bounds=[0, 2], method="bounded" ) return solution.x
SciPy’s minimize_scalar function finds the value that minimizes the negative integral. You bound it between 0 and 2 so your smallest bet is 0 and your maximum bet is 2x. This means it’s best to use leverage and buy the S&P500 index on margin.
Analyze the results
Create the optimal Kelly value through time to see how well it performs against a single share in the S&P500 index.
annual_returns['f'] = return_params.apply(get_kelly, axis=1) ( annual_returns[["^GSPC"]] .assign(kelly=annual_returns["^GSPC"].mul(annual_returns.f.shift())) .dropna() .loc["1900":] .add(1) .cumprod() .sub(1) .plot(lw=2) )
This chart shows how a portfolio performs over time by investing at the optimal position size described by the Kelly criterion. You can see it does pretty well!
You can use the Kelly criterion to help find the optimal position size when trading and investing. Make sure to take other risk metrics into consideration, too. Historic drawdown, volatility, and macroeconomic conditions are all popular.