The simplest football betting strategy I’ve ever designed
Introduction
Since the draw tends to be overlooked by most bettors, backing it can be a profitable strategy. However, I don’t think doing it blindly would be a good idea. In this article, I will present a simple strategy that delivered a good ROI for me in the 2019–2020 season. The idea is simple: back the draw in the 2. Bundesliga, the second division of professional football in Germany, when the draw odds are below 4. It seems too good to be true. But it’s true!
It works because in this league odds are underpriced by punters. What this means is that the draw in 2. Bundesliga happens more frequently than betting markets predict. I noticed this pattern and exploited the inefficiency during the 2019–2020 season. Then, Covid-19 came and I withdrew all funds from Betfair. But that is another story.
Python code
I decided to look at the strategy and to extend the analysis back to the 2014/2015 season. There are many good resourceson football data. I like and use https://www.football-data.co.uk/downloadm.php by Joseph Buchdahl.
Once we got the dataset, we’re ready to write some Python code to analyze the strategy. Below is the small script to do this task:
d2 = data[data[‘Div’] == ‘D2’].reset_index(drop=True) d2['result'] = 0.0 for i in range(len(d2)): if 3 < d2["PSCD"][i] <= 4: if d2["FTR"][i] == 'D': d2['result'][i] = d2["PSCD"][i] - 1 else: d2['result'][i] = -1
First, we select a subset of the dataset where ‘Div’ is ‘D2’, i.e., 2. Bundesliga. We then add a column to this data frame. The column on which we’ll make a decision is “PSCD”, Pinnacle closing draw odds. Pinnacle is a leading bookmaker known for best odds that attract the sharpest bettors. We’ll bet on the draw if the odds are between 3 and 4. This is because the draw odds below 3 are seldom a value bet. If the match resulted in the draw, we win; otherwise, we lose our initial investment. That’s it. This is a simple “back the draw when odds are below 4” strategy.
Results
The following line displays the profit this strategy would deliver if we backed $1 on each qualified match. Round function rounds the result to two decimal places.
print(round(sum(d2[‘result’]), 2))
Then we select the qualified matches, i.e., where the result is NOT equal to 0. If the result is zero, it implies that we didn’t bet on this particular match.
matches = d2[d2[‘result’] != 0]
Now that we have profit and the number of matches, we can find the yield of the strategy. Yield or ROI is simply the profit divided by the number of matches on which we bet. The following code does this and displays the answer as percentage with two decimal places.
print(“{:.2%}”.format(sum(d2[‘result’]) / len(matches)))
Over 9 complete and one incomplete season, the strategy delivered 28.72 points with 1.14% ROI. What we can do now is to analyze profit per season. Perhaps we can find some interesting pattern there. You can do this with the following line (by the way, it’s great that Python allows you to accomplish so many things with such little code):
d2.groupby(d2[‘season’])[‘result’].sum()
This is what you’ll see after you run this code:
season
2014–2015 40.31
2015–2016 4.71
2016–2017 -3.10
2017–2018 -2.89
2018–2019 13.86
2019–2020 56.46
2020–2021 -39.46
2021–2022 2.98
2022–2023 -27.99
2023–2024 -16.16
As they say, a picture is worth a thousand words. Let’s visualize the result with the following one-liner.
plt.plot(d2.groupby(d2[‘season’])[‘result’].sum())
We can also look at cumulative returns:
plt.plot(d2[‘Date’], d2[‘result’].cumsum())
which produces the following plot:
We see that the “back the draw under 4” strategy was highly profitable until 2021 after which it went to negative zone. Why did the performance of such a lucrative strategy decrease? It can be explained by one of the two factors: either markets become more efficient and the draw odds started to be priced fairly or the number of draws declined during the last 3–4 seasons.
First, let’s look at how the draw dynamics changed over years in 2. Bundesliga:
draw = d2[d2[‘FTR’] == ‘D’] draw.groupby(d2['season'])['result'].count()
After selecting matches which finished with the draw, we group the result per season. This is the result we get:
season
2014–2015 97
2015–2016 86
2016–2017 88
2017–2018 89
2018–2019 88
2019–2020 98
2020–2021 72
2021–2022 86
2022–2023 73
2023–2024 52
Name: result, dtype: int64
Now, let’s put it on a chart.
plt.plot(draw.groupby(d2[‘season’])[‘result’].count())
We see that the number of draws in this league did decrease. So, this could explain the lackluster performance of the strategy since 2021? But let’s not jump to conclusions without analyzing the frequence of draws in this particular league. The following lines will group the average draw odds per season and plot the result:
draw.groupby(d2[‘season’])[‘PSCD’].mean() plt.plot(draw.groupby(d2['season'])['PSCD'].mean())
It’s clear that the number of draws in 2. Bundesliga increased not declined. However, we’re more interested in the matches where draw odds were below 4. Let’s do this. The following code will filter out the matches with this criterion, group their draw average by season and plot it.
filtered_draw = draw[draw[‘PSCD’] < 4] filtered_draw.groupby(draw['season'])['PSCD'].mean() plt.plot(filtered_draw.groupby(draw['season'])['PSCD'].mean())
Even in this case, the draw odds were in uptrend. This is the opposite of what we’d expect if the reason of the underperformance of the strategy was the increasing efficiency of the draw market in 2. Bundesliga. So, we can carefully conclude that the reason why this strategy delivered negative results was that draws happened less frequently since 2021. But this is actually a good thing. It means that the market still remains inefficient. And when the dearth of draws in 2. Bundesliga is over, we can still make money with this simple strategy.
P. S.
If you want to delve into football betting, I wrote a book “Building a football betting model” which introduces sports betting in an easy way. You can find the book at https://fmiren.gumroad.com/l/eixemq. Your support for my work would mean the world to me.