When talking about risk in sports betting and trading, most people will associate risk with losing money. People are concerned about how much they might lose as well as how long they would need to recover their losses. In finance it is quite common to use a metric for this which is called the drawdown - a measure for downside volatility.
When assessing betting or trading strategies it makes sense to consider and calculate the drawdown among other KPIs. When talking about a drawdown we are primarly interested in a) how long it lasted (duration) and b) how much was lost (magnitude). When evaluating betting strategies on historical data we can easily calculate the maximum drawdown (MDD) for the past using a programming language like Python or tools like Excel.
How to Calculate Drawdown with Python
First I would like to cover how the drawdown figure can be calculated with a programming language like Python. For this I will use the backtest results for the "Back the Newcomer in Horse Racing" strategy but of course you could take the result of any backtested betting or trading strategy. All you need is a Pandas dataframe with profit and loss figures along with the timestamp they occurred. The dataframe could look like the following example:
|47128||2014-01-01 12:55:00||112339739_pricesukwin_GB / Fake 1st Jan_2m Mdn...||Polstar||-1.00|
|47127||2014-01-01 13:55:00||112339761_pricesukwin_GB / Chelt 1st Jan_3m Hc...||Astigos||-1.00|
|47126||2014-01-01 13:55:00||112339761_pricesukwin_GB / Chelt 1st Jan_3m Hc...||Grand Vision||-1.00|
|47125||2014-01-01 14:05:00||112339743_pricesukwin_GB / Fake 1st Jan_2m4f H...||Bantry Bere||-1.00|
|47124||2014-01-01 14:10:00||112339709_pricesukwin_GB / Muss 1st Jan_2m Hca...||Streets Of Newyork||-1.00|
|47123||2014-01-01 14:35:00||112337408_pricesukwin_GB / Sthl 1st Jan_5f Mdn...||Apophenia||-1.00|
|47122||2014-01-01 14:35:00||112337408_pricesukwin_GB / Sthl 1st Jan_5f Mdn...||Ruby Looker||4.41|
|47121||2014-01-01 15:25:00||112339679_pricesukwin_GB / Catt 1st Jan_3m1f N...||Ballythomas||-1.00|
|47120||2014-01-01 15:40:00||112339767_pricesukwin_GB / Chelt 1st Jan_1m6f NHF||Solstice Star||-1.00|
|47119||2014-01-01 15:40:00||112339767_pricesukwin_GB / Chelt 1st Jan_1m6f NHF||Sandy Beach||-1.00|
First we need to ensure that it is actually sorted in terms of time:
Next I will calculate the cumulative profit by simply adding up all the profit and losses:
profit_cumsum = df.profit.cumsum()
In order to calculate the drawdown I will use the method described in a stackoverflow.com post. For more details on the method please refer to the original post.
k = np.argmax(np.maximum.accumulate(profit_cumsum) - profit_cumsum) l = np.argmax(profit_cumsum[:k]) low = profit_cumsum[k] high = profit_cumsum[l] drawdown = high-low
For my data set the drawdown results in a value of around 905 which is identical with the reported drawdown for the "Back the Newcomer in Horse Racing" backtest. We could successfully calculate the drawdown for the period of the backtest. However, we do not know what that means for the drawdowns in the future. We cannot predict the drawdown in the future but we can use a tool called "Monte Carlo simulation" that helps us to assess drawdown scenarios.
What is a Monte Carlo Method?
If you have never heard of the Monte Carlo Method then I would suggest to read through the wikipedia page. Monte Carlo Methods typically use random sampling and have the following pattern (as described in the wiki page):
- Define a domain of possible inputs
- Generate inputs randomly from a probability distribution over the domain
- Perform a deterministic computation on the inputs
- Aggregate the results
Estimating Drawdown Using Monte Carlo Method
The question is now: How can we use the Monte Carlo method to simulate drawdowns? It is rather easy, we just follow the pattern:
- Possible inputs are profit and loss figures from historical data
- Use uniform random sampling from the past profit and loss figures in multiple iteration
- For each iteration calculate the drawdown
- Aggregate results from multiple iterations
With iteration I basically refer to a possible equity curve (accumulated profit and loss). By backtesting a betting or trading strategy we obtain exactly one realisation (the one that happened in the past). By simulating additional equity curves we randomly create scenarios that are then used to look at certain statistics, like the drawdown for instance. Well, this might sound a bit complex to begin with but it should become much clearer once we start with some Python implementation.
Using Python to Run a Monte Carlo Method
Again I will use the Monte Carlo simulation to assess the "Back the Newcomer in Horse Racing" strategy in more detail. We start with a Python iterable that contains our profit and losses for the period the strategy was backtested on. In my case I use the profit column of the dataframe which is basically a numpy array with 47.129 entries (number of bets). First I will fix the random seed which is important when working with random numbers. By fixing the seed I can ensure that every time I run the program I get the same result which matters for reproducibility. I will then define the number of iterations (number of scenarios) as well as the number of samples per iteration which I set identical to the number of samples in the backtest:
import numpy as np np.random.seed(0) n_iterations = 4000 n_samples = len(profit)
Next I will initialize a variable called simulated_profit_loss as a numpy array with zeros. Then I will loop over the scenarios and simulate profits by drawing samples from the original profit iterable. The sampling is happening with replacement. The values that are sampled are then written into the simulated_profit_loss array. The last step is to apply the cumsum method to get cumulative profit and loss:
simulated_profit_loss = np.zeros((n_iterations, n_samples), dtype=np.float32) for i in range(n_iterations): simulated_profit_loss[i, ...] = np.random.choice(profit, size=n_samples, replace=True) simulated_cum_profit_loss = np.cumsum(simulated_profit_loss, axis=1)
When I plot some of the cumulative profit and loss scenarios I will obtain something like the following chart:
For every simulated scenario I will then calculate the maximum drawdown using the method mentioned above and save them to a list or numpy array. I can then create a histogram showing how the maximum drawdown is distributed:
On the x-axis the drawdown is shown and for the 4.000 simulations that were used it ranges from around 500 to 3500 units. On the y-axis the frequency is illustrated, the peak is at around 1000 units drawdown which is just above the value from my backtest (905).
Simulation Results and Conclusion
Instead of relying on a single drawdown value from a backtest we have now generated a probability distribution for the maximum drawdown using a Monte Carlo method. We are now in the position where we can calculate some statistics of the distribution. The mean value (average of maximum drawdown across all the scenarios) is around 1100 units. The median value is around 1050 units (half of the simulated scenarios have a larger drawdown than this). Both values are relatively close but above the maximum drawdown of our backtest.
When we shift our attention more towards the worst case scenario we could look at the 95th percentile which is 1878 units for this strategy. This means that in 95% of the simulated cases the maximum drawdown is below this value. From a statistical point of view it is very unlikely to experience such a drawdown and the 95% confidence level is commonly used in statistical testing. Depending on your preferences you might want to choose a different confidence level.
Anyway, when you apply a betting strategy in practice you will always face drawdowns. It is very likely that you find yourself in a situation where the drawdown that you actually experience is larger than the one obtained from the backtest. Many traders and punters give up in periods of larger drawdown. The Monte Carlo simulation can help you to get an idea of what drawdowns to expect which can help you to stick to a well working betting or trading strategy instead of abandoning it due to emotions and fear.