The Kelly Criterion
The bet of Medallion fund ?
The Kelly Criterion is a formula used for bankroll management in gambling and investing. It helps determine the optimal bet size to maximize long-term growth, while minimizing the risk of losing the entire bankroll.
In [367]:
def kelly_criterion(win_probability, odds):
return (win_probability * (odds + 1) - 1) / odds
In [368]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# Example win probability and odds
win_probability = 0.6
odds = 2.0
# Calculate the recommended bet size
win_probabilities = np.linspace(0.01, 0.99, 100)
bet_sizes = [(kelly_criterion(p, odds) * 100) for p in win_probabilities]
# Plot the recommended bet size as a function of win probability
plt.figure(figsize=(10, 6))
plt.plot(win_probabilities, bet_sizes)
plt.xlabel('Win Probability')
plt.ylabel('Recommended Bet Size (%)')
plt.title('Kelly Criterion Recommended Bet Size vs. Win Probability')
plt.grid(True)
# Highlight the point for the example win probability
recommended_bet_size = kelly_criterion(win_probability, odds)
plt.scatter(win_probability, recommended_bet_size * 100, color='red', label='Example (60%)')
plt.legend()
# Display the graph
plt.show()
# show result
df_res = pd.DataFrame({
'Win Probability':win_probabilities,
'bet_sizes':bet_sizes
})
print(df_res)
Win Probability bet_sizes 0 0.010000 -48.500000 1 0.019899 -47.015152 2 0.029798 -45.530303 3 0.039697 -44.045455 4 0.049596 -42.560606 .. ... ... 95 0.950404 92.560606 96 0.960303 94.045455 97 0.970202 95.530303 98 0.980101 97.015152 99 0.990000 98.500000 [100 rows x 2 columns]
In [369]:
def run_simulations(num_simulations, num_rounds, initial_bankroll, win_probability=None, odds=None):
# Arrays to store results
bankroll_progressions = []
final_bankrolls = []
for _ in range(num_simulations):
if win_probability == None:
# Generate random win probability (between 0.01 and 0.99)
win_probability = np.random.uniform(0.01, 0.99)
fixed_probability = False
else:
fixed_probability = True
if odds == None:
# Generate random odds (between 1.0 and 3.0)
odds = np.random.uniform(1.0, 3.0)
# Initialize bankroll and progression for this simulation
bankroll = initial_bankroll
bankroll_progression = [bankroll]
# Simulate betting and update bankroll progression
for _ in range(num_rounds):
if bankroll > 0:
# Calculate recommended bet size
recommended_bet_size = kelly_criterion(win_probability, odds)
if recommended_bet_size <= 0:
# do nothing
bankroll_progression.append(bankroll)
else:
outcome = np.random.choice([-1, 1], p=[1 - win_probability, win_probability])
bankroll *= (1 + recommended_bet_size * outcome)
bankroll_progression.append(bankroll)
else:
bankroll_progressions.append(0)
# Store bankroll progression for this simulation
bankroll_progressions.append(bankroll_progression)
# Store the final bankroll for this simulation
final_bankrolls.append(bankroll)
return bankroll_progressions, final_bankrolls, fixed_probability
In [370]:
import random
# Number of simulations
num_simulations = 25
# Number of rounds or iterations in each simulation
num_rounds = 100
# Initial bankroll
initial_bankroll = 100
# win_probability
win_probability = 0.6
# odds
odds = 2
bankroll_progressions, final_bankrolls, fixed_probability = run_simulations(num_simulations, num_rounds, initial_bankroll, win_probability=win_probability, odds=odds)
if not fixed_probability:
# Plot the recommended bet sizes (optional, for variable win probabilities)
plt.figure(figsize=(10, 6))
plt.hist(bet_sizes, bins=20, alpha=0.7, color='blue')
plt.xlabel('Recommended Bet Size')
plt.ylabel('Frequency')
plt.title('Distribution of Recommended Bet Sizes')
plt.grid(True)
# Plot the min, max, average bankroll progression the batch of simulation
plt.figure(figsize=(10, 6))
mean_bankrolls = np.mean(bankroll_progressions, axis=0)
plt.plot(range(num_rounds + 1), mean_bankrolls, color='green', linewidth=2)
plt.xlabel('Number of Rounds {0}'.format(num_rounds))
plt.ylabel('Average Bankroll')
plt.title('Average Bankroll Progression for {0} Simulations with a win rate of {1}'.format(num_simulations, win_probability))
plt.grid(True)
# Plot the bankroll progression for a random simulation
plt.figure(figsize=(10, 6))
progression_selected = random.randint(0, len(bankroll_progressions)-1)
rand_progression = bankroll_progressions[progression_selected]
plt.plot(range(num_rounds + 1), rand_progression)
plt.xlabel('Simulation progression n°{0}'.format(progression_selected))
plt.ylabel('Bankroll')
plt.title('Bankroll Progression for simulation n°{0} with a win rate of {1}'.format(progression_selected, win_probability))
plt.grid(True)
plt.show()
In [371]:
# return
def evaluate(data, initial_bankroll):
df = pd.DataFrame(data)
#(initial_bankroll - df['Final Bankroll'])*-1
df['Profit'] = df['Final Bankroll'].apply(lambda x: (initial_bankroll - x)*-1 if x > 0 else 0)
df['Win/Loss'] = df['Profit'].apply(lambda x: 'win' if x > 0 else ('loss' if x < 0 else '#N/A'))
# nb profit vs nb loss
profitRate = (df['Profit'] > 0).sum() / len(df)
bankrupts = (df['Final Bankroll'] == 0).sum()
return df, profitRate, bankrupts
In [372]:
# Plot the final bankrolls
plt.figure(figsize=(10, 6))
plt.hist(final_bankrolls, bins=20, alpha=0.7, color='green')
plt.xlabel('Final Bankroll')
plt.ylabel('Frequency')
plt.title('Distribution of Final Bankrolls')
plt.grid(True)
plt.show()
# Create a DataFrame to store the final bankrolls
data = {'Final Bankroll': final_bankrolls}
df, profitRate,bankrupts = evaluate(data, initial_bankroll)
print(df.head(25))
Final Bankroll Profit Win/Loss 0 1.132005 -98.867995 loss 1 0.485145 -99.514855 loss 2 994.632193 894.632193 win 3 374541.554786 374441.554786 win 4 1.132005 -98.867995 loss 5 5415.219719 5315.219719 win 6 994.632193 894.632193 win 7 2.641344 -97.358656 loss 8 12635.512677 12535.512677 win 9 0.485145 -99.514855 loss 10 33.554855 -66.445145 loss 11 14.380652 -85.619348 loss 12 426.270940 326.270940 win 13 29482.862913 29382.862913 win 14 78.294662 -21.705338 loss 15 78.294662 -21.705338 loss 16 14.380652 -85.619348 loss 17 14.380652 -85.619348 loss 18 374541.554786 374441.554786 win 19 994.632193 894.632193 win 20 2.641344 -97.358656 loss 21 0.207919 -99.792081 loss 22 2320.808451 2220.808451 win 23 14.380652 -85.619348 loss 24 78.294662 -21.705338 loss
In [373]:
print("\n******************************************************************\n")
# Profits ?
print("Estimated profit rate : {0}\n".format(profitRate))
# Bankrupts ?
if bankrupts == 0:
print("No bankrupts \n")
elif bankrupts/len(df) <= 0.05:
print("Take care, bankrupt can occurs ({0} times with this simulation\n)".format(bankrupts))
else:
#Bankrupts/nbRecord > 0.05:
print("Forget this, try something else...\n")
print("******************************************************************\n")
****************************************************************** Estimated profit rate : 0.4 No bankrupts ******************************************************************
In [374]:
# List of win probabilities to test
win_probabilities_to_test = np.linspace(0.01, 0.99, 10)
all_results = []
for win_probability in win_probabilities_to_test:
bankroll_progressions, final_bankrolls, _ = run_simulations(num_simulations, num_rounds, initial_bankroll, win_probability=win_probability, odds=odds)
df, profitRate, bankrupts = evaluate({'Final Bankroll':final_bankrolls}, initial_bankroll)
all_results.append([win_probability, final_bankrolls, bankroll_progressions, [df], profitRate, bankrupts])
# Plot the results
label = []
plt.figure(figsize=(12, 6))
for result in all_results:
label.append("Win Probability: {:.2f}".format(result[0]))
for progression in result[2]:
plt.plot(range(num_rounds + 1), progression, label=label)
plt.xlabel('Number of Rounds')
plt.ylabel('Bankroll')
plt.title('Bankroll Progression regarding Win Probabilities')
plt.legend(label)
plt.grid(True)
In [375]:
df_result = pd.DataFrame(
{'Win probability':[proba[0] for proba in all_results],
'Profit rate':[profit[4] for profit in all_results],
'Nb Bankrupt':[bankrupt[5] for bankrupt in all_results]}
)
print(df_result)
Win probability Profit rate Nb Bankrupt 0 0.010000 0.00 0 1 0.118889 0.00 0 2 0.227778 0.00 0 3 0.336667 0.00 0 4 0.445556 0.00 0 5 0.554444 0.28 0 6 0.663333 0.68 0 7 0.772222 1.00 0 8 0.881111 1.00 0 9 0.990000 1.00 0
In [376]:
# Plot the distribution of final bankrolls
plt.figure(figsize=(12, 6))
for final_bankrolls in all_results:
result = final_bankrolls[1]
plt.hist(result, bins=20, alpha=0.7)
plt.xlabel('Final Bankroll')
plt.ylabel('Frequency')
plt.title('Distribution of Final Bankrolls\n Win Probabilities {:.2f}'.format(final_bankrolls[0]))
plt.grid(True)
plt.show()
print(final_bankrolls[3])
[ Final Bankroll Profit Win/Loss 0 100 0 #N/A 1 100 0 #N/A 2 100 0 #N/A 3 100 0 #N/A 4 100 0 #N/A 5 100 0 #N/A 6 100 0 #N/A 7 100 0 #N/A 8 100 0 #N/A 9 100 0 #N/A 10 100 0 #N/A 11 100 0 #N/A 12 100 0 #N/A 13 100 0 #N/A 14 100 0 #N/A 15 100 0 #N/A 16 100 0 #N/A 17 100 0 #N/A 18 100 0 #N/A 19 100 0 #N/A 20 100 0 #N/A 21 100 0 #N/A 22 100 0 #N/A 23 100 0 #N/A 24 100 0 #N/A]
[ Final Bankroll Profit Win/Loss 0 100 0 #N/A 1 100 0 #N/A 2 100 0 #N/A 3 100 0 #N/A 4 100 0 #N/A 5 100 0 #N/A 6 100 0 #N/A 7 100 0 #N/A 8 100 0 #N/A 9 100 0 #N/A 10 100 0 #N/A 11 100 0 #N/A 12 100 0 #N/A 13 100 0 #N/A 14 100 0 #N/A 15 100 0 #N/A 16 100 0 #N/A 17 100 0 #N/A 18 100 0 #N/A 19 100 0 #N/A 20 100 0 #N/A 21 100 0 #N/A 22 100 0 #N/A 23 100 0 #N/A 24 100 0 #N/A]
[ Final Bankroll Profit Win/Loss 0 100 0 #N/A 1 100 0 #N/A 2 100 0 #N/A 3 100 0 #N/A 4 100 0 #N/A 5 100 0 #N/A 6 100 0 #N/A 7 100 0 #N/A 8 100 0 #N/A 9 100 0 #N/A 10 100 0 #N/A 11 100 0 #N/A 12 100 0 #N/A 13 100 0 #N/A 14 100 0 #N/A 15 100 0 #N/A 16 100 0 #N/A 17 100 0 #N/A 18 100 0 #N/A 19 100 0 #N/A 20 100 0 #N/A 21 100 0 #N/A 22 100 0 #N/A 23 100 0 #N/A 24 100 0 #N/A]
[ Final Bankroll Profit Win/Loss 0 88.581158 -11.418842 loss 1 83.422551 -16.577449 loss 2 81.770660 -18.229340 loss 3 85.963168 -14.036832 loss 4 86.827119 -13.172881 loss 5 86.827119 -13.172881 loss 6 77.782626 -22.217374 loss 7 95.004072 -4.995928 loss 8 88.581158 -11.418842 loss 9 85.963168 -14.036832 loss 10 85.107813 -14.892187 loss 11 80.957022 -19.042978 loss 12 85.107813 -14.892187 loss 13 82.592476 -17.407524 loss 14 85.107813 -14.892187 loss 15 87.699753 -12.300247 loss 16 81.770660 -18.229340 loss 17 87.699753 -12.300247 loss 18 88.581158 -11.418842 loss 19 85.963168 -14.036832 loss 20 88.581158 -11.418842 loss 21 81.770660 -18.229340 loss 22 85.107813 -14.892187 loss 23 86.827119 -13.172881 loss 24 85.963168 -14.036832 loss]
[ Final Bankroll Profit Win/Loss 0 33.374949 -66.625051 loss 1 2.200295 -97.799705 loss 2 6.100048 -93.899952 loss 3 3.090995 -96.909005 loss 4 1.566258 -98.433742 loss 5 3.090995 -96.909005 loss 6 23.757631 -76.242369 loss 7 4.342260 -95.657740 loss 8 0.564951 -99.435049 loss 9 1.114926 -98.885074 loss 10 2.200295 -97.799705 loss 11 6.100048 -93.899952 loss 12 65.865131 -34.134869 loss 13 23.757631 -76.242369 loss 14 12.038384 -87.961616 loss 15 4.342260 -95.657740 loss 16 6.100048 -93.899952 loss 17 3.090995 -96.909005 loss 18 23.757631 -76.242369 loss 19 1.114926 -98.885074 loss 20 0.793649 -99.206351 loss 21 2.200295 -97.799705 loss 22 92.527969 -7.472031 loss 23 4.342260 -95.657740 loss 24 23.757631 -76.242369 loss]
[ Final Bankroll Profit Win/Loss 0 36.749328 -63.250672 loss 1 145.899639 45.899639 win 2 0.294736 -99.705264 loss 3 73.223724 -26.776276 loss 4 18.443655 -81.556345 loss 5 36.749328 -63.250672 loss 6 0.147921 -99.852079 loss 7 73.223724 -26.776276 loss 8 0.147921 -99.852079 loss 9 18.443655 -81.556345 loss 10 18.443655 -81.556345 loss 11 290.707760 190.707760 win 12 290.707760 190.707760 win 13 18.443655 -81.556345 loss 14 36.749328 -63.250672 loss 15 2299.661134 2199.661134 win 16 4.645604 -95.354396 loss 17 2299.661134 2199.661134 win 18 0.004710 -99.995290 loss 19 73.223724 -26.776276 loss 20 145.899639 45.899639 win 21 18.443655 -81.556345 loss 22 0.587266 -99.412734 loss 23 579.240650 479.240650 win 24 0.294736 -99.705264 loss]
[ Final Bankroll Profit Win/Loss 0 35.712998 -64.287002 loss 1 312.986743 212.986743 win 2 623698.420672 623598.420672 win 3 1.376502 -98.623498 loss 4 312.986743 212.986743 win 5 71166.402940 71066.402940 win 6 35.712998 -64.287002 loss 7 2742.998510 2642.998510 win 8 4.074991 -95.925009 loss 9 623698.420672 623598.420672 win 10 8120.361924 8020.361924 win 11 105.724619 5.724619 win 12 71166.402940 71066.402940 win 13 926.564714 826.564714 win 14 12.063588 -87.936412 loss 15 2742.998510 2642.998510 win 16 0.053055 -99.946945 loss 17 24039.487281 23939.487281 win 18 8120.361924 8020.361924 win 19 24039.487281 23939.487281 win 20 71166.402940 71066.402940 win 21 4.074991 -95.925009 loss 22 12.063588 -87.936412 loss 23 71166.402940 71066.402940 win 24 312.986743 212.986743 win]
[ Final Bankroll Profit Win/Loss 0 2.775327e+05 2.774327e+05 win 1 9.774072e+12 9.774072e+12 win 2 8.548066e+10 8.548066e+10 win 3 4.148939e+11 4.148939e+11 win 4 5.718010e+04 5.708010e+04 win 5 7.475844e+08 7.475843e+08 win 6 7.475844e+08 7.475843e+08 win 7 5.718010e+04 5.708010e+04 win 8 3.628519e+09 3.628519e+09 win 9 1.347049e+06 1.346949e+06 win 10 3.173378e+07 3.173368e+07 win 11 2.775327e+05 2.774327e+05 win 12 7.475844e+08 7.475843e+08 win 13 9.774072e+12 9.774072e+12 win 14 3.173378e+07 3.173368e+07 win 15 1.178082e+04 1.168082e+04 win 16 3.628519e+09 3.628519e+09 win 17 1.761159e+10 1.761159e+10 win 18 3.628519e+09 3.628519e+09 win 19 1.540249e+08 1.540248e+08 win 20 2.775327e+05 2.774327e+05 win 21 2.775327e+05 2.774327e+05 win 22 3.173378e+07 3.173368e+07 win 23 2.013753e+12 2.013753e+12 win 24 2.632823e+16 2.632823e+16 win]
[ Final Bankroll Profit Win/Loss 0 7.760195e+10 7.760195e+10 win 1 9.005904e+17 9.005904e+17 win 2 8.449250e+14 8.449250e+14 win 3 8.816393e+16 8.816393e+16 win 4 7.596897e+09 7.596897e+09 win 5 8.630869e+15 8.630869e+15 win 6 8.271452e+13 8.271452e+13 win 7 8.630869e+15 8.630869e+15 win 8 8.816393e+16 8.816393e+16 win 9 9.005904e+17 9.005904e+17 win 10 8.630869e+15 8.630869e+15 win 11 8.816393e+16 8.816393e+16 win 12 8.271452e+13 8.271452e+13 win 13 9.397235e+19 9.397235e+19 win 14 8.449250e+14 8.449250e+14 win 15 9.199489e+18 9.199489e+18 win 16 9.199489e+18 9.199489e+18 win 17 9.005904e+17 9.005904e+17 win 18 7.927003e+11 7.927003e+11 win 19 9.805570e+21 9.805570e+21 win 20 8.816393e+16 8.816393e+16 win 21 9.397235e+19 9.397235e+19 win 22 9.199489e+18 9.199489e+18 win 23 8.449250e+14 8.449250e+14 win 24 9.005904e+17 9.005904e+17 win]
[ Final Bankroll Profit Win/Loss 0 5.971056e+31 5.971056e+31 win 1 2.576578e+25 2.576578e+25 win 2 4.512133e+29 4.512133e+29 win 3 3.409672e+27 3.409672e+27 win 4 3.409672e+27 3.409672e+27 win 5 5.971056e+31 5.971056e+31 win 6 4.512133e+29 4.512133e+29 win 7 4.512133e+29 4.512133e+29 win 8 5.971056e+31 5.971056e+31 win 9 1.947037e+23 1.947037e+23 win 10 3.409672e+27 3.409672e+27 win 11 3.409672e+27 3.409672e+27 win 12 4.512133e+29 4.512133e+29 win 13 3.409672e+27 3.409672e+27 win 14 2.576578e+25 2.576578e+25 win 15 3.409672e+27 3.409672e+27 win 16 4.512133e+29 4.512133e+29 win 17 4.512133e+29 4.512133e+29 win 18 5.971056e+31 5.971056e+31 win 19 5.971056e+31 5.971056e+31 win 20 2.576578e+25 2.576578e+25 win 21 4.512133e+29 4.512133e+29 win 22 4.512133e+29 4.512133e+29 win 23 3.409672e+27 3.409672e+27 win 24 4.512133e+29 4.512133e+29 win]