# Stake sizing, Part 1

In the last post we used Python code to take a look at a classic gambling situation, the coin flip, to make a point about the importance of choosing the highest odds available to bet at. Today, we’ll again use the coin flipping example to investigate another fundamental principal of successful gambling: stake sizing.

Now, imagine we’re one of the lucky punters from the last post who were allowed to bet on a fair coin flip at odds of 2.03. As I stated then, this is pretty much like a license to print money – but how much of your bankroll should you bet on each flip of the coin? Knowing that the coin was indeed fair and you would be getting the best of it, a natural instinct could be to bet as much as you could possibly cough up, steal and borrow in order to maximize your profit. This is a poor strategy though, as we’ll soon come to see.

The reason for this is that even if we do have come across a profitable proposition, our edge when betting at a (I’ll empasize it again: fair) coin flip at 2.03 odds is only 1.5% – meaning that for each 1 unit bet we are expected to net 0.015 units on average. This conclusion should be absolute basics for anyone interested in serious gambling, but to make sure we’re all on the same page I’ll throw some maths at you:

The Expected Value, or EV, of any bet is, simply put, the sum of all outcomes multiplied by their respective probabilities – indicating the punter’s average profit or loss on each bet. So with our coin flip, we’ll win a net of 1.03 units 50% of the time and lose 1 unit 50% of the time; our EV is therefore 1.03 * 0.5 + (-1 * 0.5) = 0.015, for a positive edge of 1.5% and an average profit of 0.015 units per bet. For these simple types of bets though, an easier way to calculate EV is to divide the given odds by the true odds and subtract 1: 2.03 / 2.0 – 1 = 0.015.

An edge of only 1.5% is nothing to scoff at though, empires has been built on less, so we’ll definitely want to bet something – but how much?

Stake sizing is much down to personal preferences about risk aversion and tolerance of the variance innately involved in gambling, but with some Python code we can at least have a look at some different strategies before we set out to chase riches and glory flipping coins. Just like in the last post I’ll just give you the code with some comments in it, which will hopefully guide you along what’s happening  before I briefly explain it.

Here we go:

```import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

def coin_flips(n=10000,odds=1.97,bankroll=100,stake=1,bankrupt=False):
'''
Simulates 10000 coinflips for a single punter, betting at 1.97 odds,
also calculates net winnings

NEW: default bankroll and stake set at 100 and 1, respectively
now also calculates if player went bankrupt or not
'''

# create a pandas dataframe for storing coin flip results
# and calculate net winnings
df = pd.DataFrame()
# insert n number of coinflips, 0=loss, 1=win
df['result'] = np.random.randint(2,size=n)
# calculate net winnings
df['net'] = np.where(df['result']==1,stake*odds-stake,-stake)
# calculate cumulative net winnings
df['cum_net'] = df['net'].cumsum()

# calculate total bankroll
df['bankroll'] = df['cum_net'] + bankroll

# if bankroll goes below the default stake, punter will stop betting
# count times bankroll < stake
df['bankrupt'] = np.where(df['bankroll']<stake,1,0)
# count cumulative bankruptcies, with column shifted one step down
df['bankruptcies'] = df['bankrupt'].cumsum().shift(1)
# in case first flip is a loss, bankruptcies will be NaN, replace with 0
df.fillna(0,inplace=True)
# drop all flips after first bankruptcy
if bankrupt:
df = df[df['bankruptcies']==0]

return df
```

First off, we’ll modify our original coin_flips function to take our punter’s bankroll and stake size into consideration, setting the bankrupt threshold at the point where a default sized bet can no longer be made. By default, our punter will have an endless stream of 100 unit bankrolls, but if we set the parameter bankrupt to True, the function will cut away any coin flips after his first bankruptcy.

```def many_coin_flips(punters=100,n=10000,odds=1.97,bankroll=100,stake=1,color='r',plot=False,bankrupt=False):
'''
Simulates 10000 coinflips for 100 different punters,
all betting at 1.97 odds,
also calculates and plots net winnings for each punter

NEW: now also saves punter bankruptcies
'''

# create pandas dataframe for storing punter results
punter_df = pd.DataFrame()
# loop through all punters
for i in np.arange(punters):
# simulate coin flips
df = coin_flips(n,odds,bankroll,stake,bankrupt)
# calculate net
net = df['net'].sum()
# check for bankruptcy
bankruptcy = df['bankrupt'].sum()

# append to our punter dataframe
punter_df = punter_df.append({'odds':odds,
'net':net,
'bankrupt':bankruptcy},ignore_index=True)

if plot:
# plot the cumulative winnings over time
df['cum_net'].plot(color=color,alpha=0.1)

# check if punters ended up in profit
punter_df['winning'] = np.where(punter_df['net']>0,1,0)

return punter_df
```

We also want to modify the many_coin_flips function so that it’ll also take bankroll and stake size into consideration, counting up how many of our punters went bankrupt.

We won’t use the compare_odds function here, instead we’ll write a new one to compare stake sizing – but if we ever want to use it again sometime in the future a few minor changes will be needed here as well:

```def compare_odds(punters=100,n=10000,odds=[1.97,2.00,2.03]):
'''
Simulates and compare coin flip net winnings
after 10000 flips for 3 groups of punters,
betting at odds of 1.97, 2.00 and 2.03, respectively.
Also plots every punters net winnings
'''

# create figure and ax objects to plot on
fig, ax = plt.subplots()

# set y coordinates for annotating text for each group of punters
ys = [0.25,0.5,0.75]
# assign colors to each group of punters
cs = ['r','y','g']

# loop through the groups of punters, with their respective odds,
# chosen color and y for annotating text
for odd, color, y in zip(odds,cs,ys):
# run coin flip simulation with given odds, plot with chosen color
df = many_coin_flips(punters,n,odd,color=color,plot=True)
# calculate how many punters in the group ended up in profit
winning_punters = df['winning'].mean()
# set a text to annotate
win_text = '%.2f: %.0f%%' %(odd,winning_punters * 100)
# annotate odds and chance of profit for each group of punters
ax.annotate(win_text,xy=(1.02,y),
xycoords='axes fraction', color=color,va='center')

# set title
ax.set_title('Chances of ending up in profit after %s coin flips' %n)
# set x and y axis labels
ax.set_xlabel('Number of flips')
ax.set_ylabel('Net profit')
ax.annotate('odds: chance',xy=(1.02,1.0),
xycoords=('axes fraction'),fontsize=10,va='center')
# add horizontal line at breakeven point
plt.axhline(color='k',alpha=0.5)
# set y axis range at some nice number
ax.set_ylim(-450,450)

# show plot
plt.show()
```

Now, with all our previous coin flip functions taking bankroll and stake size into consideration, we can go ahead and evaluate a few stake sizing strategies with a new function:

```def compare_stakes(punters=200,n=10000,odds=2.03,stakes=[100,50,25,10,5,2,1,0.5],bankroll=100):
'''
Similar to compare_odds, but here we instead want to compare different
staking sizes for our coin flips betting at 2.03 odds

Increased number of punters in each group, from 100 to 200

Also prints out the results
'''

# pandas df to store results
results_df = pd.DataFrame(columns=['stake','win','lose','bankrupt'])

# colors to use in plot later, green=1=win, yellow=4=lost, red=2=bankrupt
colors = [sns.color_palette()[i] for i in (1,4,2)]

# loop through the groups of punters, with their respective odds
for stake in stakes:
# run coin flip simulation with given stake
df = many_coin_flips(punters,n,odds,stake=stake,bankrupt=True)
# calculate how many punters in the group ended up in profit
winning_punters = df['winning'].mean()
# ...and how many went bankrupt
bankrupt_punters = df['bankrupt'].mean()
# lost money but not bankrupt
lose = 1 - winning_punters - bankrupt_punters

# append to dataframe
results_df = results_df.append({'stake':stake,
'win':winning_punters,
'lose':lose,
'bankrupt':bankrupt_punters},ignore_index=True)

# set stake as index
results_df.set_index('stake',inplace=True)

# plot
fig = plt.figure()
# create ax object
ax = results_df.plot(kind='bar',stacked=True,color=colors,alpha=0.8)
# fix title, axis labels etc
ax.set_title('Simulation results: betting %s coin flips at %s odds, starting bankroll %s' %(n,odds,bankroll))
ax.set_ylabel('%')
# set legend outside plot
ax.legend(bbox_to_anchor=(1.2,0.5))

# add percentage annotation for both win and bankrupt
for x, w, l, b in zip(np.arange(len(results_df)),results_df['win'],results_df['lose'],results_df['bankrupt']):
# calculate y coordinates
win_y = w/2
lost_y = w + l/2
bankr_y = w + l + b/2

# annotate win, lose and bankrupt %, only if >=2%
if w >= 0.04:
ax.annotate('%.0f%%' %(w * 100),xy=(x,win_y),va='center',ha='center')
if l >= 0.04:
ax.annotate('%.0f%%' %(l * 100),xy=(x,lost_y),va='center',ha='center')
if b >= 0.04:
ax.annotate('%.0f%%' %(b * 100),xy=(x,bankr_y),va='center',ha='center')

plt.show()
```

By default, our new compare_stakes function creates a number of punter groups, all betting on fair coin flips at 2.03 odds with a starting bankroll of a 100 units. For each group and their different staking plan, the function takes note of how many ended up in profit, how many lost and how many went bankrupt.

As we can see on the plot below, the results differ substantially: Just like last time, I want to remind you that any numbers here are only rough estimates, and increasing the size of each punter group as well as the number of coin flips will get us closer to the true values.

So what can we learn from the above plot? Well, the main lesson is that even if you have a theoretically profitable bet, your edge will account for nearly nothing if you are too bold with your staking. Putting your whole bankroll at risk will see you go bankrupt around 96% of the time, and even if you bet as small as 2 units, you’ll still face a considerable risk of screwing up a lucrative proposition. The truth is that with such a small edge, keeping your bet small as well is the way to go if you want to make it in the long run.

But what if some fool offered us even higher odds, let’s say 2.20? First off, we would have to check if the person was A: mentally stable, and B: rich enough to pay us if (or rather, when) we win, before we go ahead and bet. Here our edge would be 10% (2.2 / 2.0 – 1), nearly 10 times as large as in the 2.03 situation, so we’ll likely be able to bet more – but how much? Well, the functions are written with this in mind, enabling us to play around with different situations and strategies. Specifying the odds parameter of our new function as 2.20, here’s what betting at a fair coin flip at 2.20 odds would look like: As can be seen from the new plot, with a larger edge we can go ahead and raise our stake size considerably, hopefully boosting our winnings as well. So the main take-away from this small exercise is that even if you have an edge, if you want to make it in the long run you’ll have to be careful with your staking to avoid blowing up your bankroll – but also that the larger your edge, the larger you can afford to bet.

That’s it for now, but I’ll hopefully be back soon with a Part 2 about stake sizing, looking at a staking plan that actually takes your (perceived) edge into account when calculating the optimal stake size: The Kelly Criterion.

## 2 thoughts on “Stake sizing, Part 1”

1. […] spel ligger kvar på 600 :- per spel då jag efter att ha läst denna artikel, vill sänka mitt flatbet som procentsats av rullen. mellan 0,5-0,75 % av rullen ska mitt flatbet […]

Like