• Bet or invest so as to maximize (after each bet) the expected growth rate if capital, which is equivalent to maximizing the expected value of the logarithm of wealth.

G=XXo=t=0Xt+1Xt=t=1gt

or equivalently

logG=log(t=1gt)=t=1loggt

G is the compound gain at the end of the sequence
X0 is the initial capital, or the present capital
Xt is the capital after tth trial, or the future capital
gt=Xt/Xt1 is the gain obtained after the tth trial

  • maximizing g is the same as maximizing ElogX

binary case

Elogg=E[logXnX0]1/n=plog(1+cf)+qlog(1f)

where f is the portion to invest, n is the number of trials, c is the gain in terms of % on each trial, 1 is the loss, q=1p, and t is removed as it is the same for each repetition in this example.
An optimal fraction is the one that makes the derivative null.

g(f)=p1+fq1f=pqf(1+f)(1f)=0 when f=f=pq f=(cpq)/c

from sympy import *
# For a symbolic verification with Python and SymPy one would set the derivative y'(x) 
# of the expected value of the logarithmic bankroll y(x) to 0 and solve for x
x, b, p = symbols('x b p')
y = p * log(1 + b * x) + (1 - p) * log(1 - x)
s = solve(diff(y, x), x)
print(str(s))
# [(b*p + p - 1)/b]

example: p=53,1=47, expected win is 2:1, or 100 % => f=(10.530.47)/1=0.06

example: p=53,1=47, expected win is 3:1, or 200 % => f=(20.530.47)/2=0.295


2 independent binary cases
g(f1,f2)=p1p2log(1+f1+f2) + p1q2log(1+f1f2) + q1p2log(1f1+f2) + q1q2log(1f1f2)


2 dependent binary cases


more than 2 outcomes
Example from “mathexchange/Kelly criterion with more than two outcomes” f(x)=0.7log(1x) + 0.2log(1+10x) + 0.1log(1+30x), where optimum occurs at x=0.248, with f(0.248)=0.263.
“In other words, if you bet a little under a quarter of your bankroll, you should expect your bankroll to grow on average by e0.263=1.30 for every bet.”

from sympy import *
# For a symbolic verification with Python and SymPy one would set the derivative y'(x) 
# of the expected value of the logarithmic bankroll y(x) to 0 and solve for x
x, p = symbols('x p')
y = 0.1 * log(1 + 30 * x) + 0.2 * log(1 + 10 * x) + 0.7 * log(1 - x)
s = solve(diff(y, x), x)
print(str(s))
# [-0.0578343329665600, 0.247834332966560]
d = s[1]
fx = 0.1 * log(1 + 30 * d) + 0.2 * log(1 + 10 * d) + 0.7 * log(1 - d)
print(str(fx)) # 0.263191478105847

continuous case

Elogg=log(1+fr)P(r) dr where r is the excess return of the asset to invest in (i.e. return minus a risk-free reference).
Again, an optimal fraction is the one that makes the derivative null.

ddfElogg=+r1+frP(r) dr=0

Two options for solving here with Python:

  • compute the integral in the 1st equation and maximize the fargmaxfElogg
  • compute the derivative of the integral in the 2nd equation and use numeric solver to find zero ddfEloggf=f=0

Example from E. Thorp’s article with Source code from (Leiva, 2018).
Thorp focuses on annual returns and suggests modeling P(r) as a normal distribution truncated at μ±3σ. Result is 1.197 and not 1.17 as normalization to account for truncated normal distribution was not performed.

from scipy.optimize import minimize_scalar, newton, minimize
from scipy.integrate import quad
from scipy.stats import norm

def norm_integral(f,m,st):
    val,er = quad(lambda l: np.log(1+f*l)*norm.pdf(l,m,st),m-3*st,m+3*st)
    return -val

def norm_dev_integral(f,m,st):
    val,er = quad(lambda l: (l/(1+f*l))*norm.pdf(l,m,st),m-3*st,m+3*st)
    return val

# Reference values from Eduard Thorp's article
m = .058
s = .216
# Option 1: minimize the expectation integral
sol = minimize_scalar(norm_integral,args=(m,s),bounds=[0.,2.],method='bounded')
print('Optimal Kelly fraction: {:.4f}'.format(sol.x))
# Option 2: take the derivative of the expectation and make it null
x0 = newton(norm_dev_integral,.1,args=(m,s))
print('Optimal Kelly fraction: {:.4f}'.format(x0))

Portfolio construction
Example from wduwant, with known expected return at each step.

  • Good for educational processes, but miss the point of having distributions of expected return.
  • Example is with log(g) instead of (logg)

We have 2 assets a, b and c, their weights in the portfolio wa+wb+wc=1 and their expected returns are ra, rb and rc.

logg=log(future valuepresent value)=log(XtXt1)= =log(t=1n(1+wara+wbrb+wcrc))

assuming asset returns are:
Ra=[t1=0.3,t2=0.15]
Rb=[t1=0.05,t2=0.2]
Rc=[t1=0.05,t2=0.1]

logg=log[(1+0.3wa0.05wb+0.05wc) (10.015wa0.2wb+0.1wc)]

def objective(x, sign=-1.0):
    w1=x[0]
    w2=x[1]
    w3=x[2]
    a=np.array([0.3,-.15])
    b=np.array([-0.05,0.2])
    c=np.array([0.05,0.1])
    fx=np.log(np.prod(1+w1*a+w2*b+w3*c))
    return sign*(fx)

def weight_sum(x):
    return x[0]+x[1]+x[2]-1.0

b=(-1,1) # bounds for weights
bounds=(b,b,b)

cons={'type': 'eq', 'fun': weight_sum}

x_default=np.zeros(3)
sol = minimize(objective, x_default,method='SLSQP', bounds=bounds, constraints=[cons])

#print(str(sol))
print('Kelly portfolio weights are as follows ' + str(sol.x*100))

Examples

  • https://github.com/DinodC/investing-etf-kelly.git
    single and multiple securities with ETFs based on “The Kelly Criterion in Blackjack Sports Betting and the Stock Market” using normal distributions. Uses matrix multiplication and python pandas.
  • https://github.com/jeromeku/Python-Financial-Tools.git
    see portfolio.py optimize_kelly_criterion() and the references “Kelly Criterion for Multivariate Portfolios: A Model-Free Approach”. It still uses covariance, but has other useful stuff, calculations and explanations.

References

  • Leiva, J. (2018). The Kelly Criterion. https://quantdare.com/kelly-criterion/