Quantitative Finance Asked by DickyBrown on March 2, 2021

Following along with E.P. Chan’s book, I’m attempting to calculate the maximum drawdown and the longest drawdown duration from cumulative portfolio returns. He codes it in MATLAB, but I wanted to try my hand at the same code in Python.

```
import pandas as pd
def drawdownCalculator(data):
highwatermark = data.copy()
highwatermark[:] = 0
drawdown = data.copy()
drawdown[:] = 0
drawdownduration = data.copy()
drawdownduration[:]=0
t = 1
while t <= len(data):
highwatermark[t] = max(highwatermark[t-1], data[t])
drawdown[t] = (1 + highwatermark[t])/(1 + data[t]) - 1
if drawdown[t] == 0:
drawdownduration[t] = 0
else:
drawdownduration[t] = drawdownduration[t-1] + 1
t += 1
return drawdown.max(), drawdownduration.max()
max_drawdown, max_drawdown_time = drawdownCalculator(cumulative_returns) #cumulative_returns is a Pandas series
```

I thought I had it figured out, but I’m getting the following error:

```
return self._engine.get_value(s, k, tz=getattr(series.dtype, "tz", None))
File "pandas/_libs/index.pyx", line 80, in pandas._libs.index.IndexEngine.get_value
File "pandas/_libs/index.pyx", line 88, in pandas._libs.index.IndexEngine.get_value
File "pandas/_libs/index.pyx", line 131, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 992, in pandas._libs.hashtable.Int64HashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 998, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: 0
```

Thank you in advance

I'm guessing your Series is indexed by a timestamp, which would explain why accessing by an integer doesn't work. But I can't tell for sure since you haven't shown us any data.

The good news is that I don't need that anyway. Here is a more idiomatic way to compute what you want:

```
highwatermarks = cumulative_returns.cummax()
drawdowns = (1 + highwatermarks)/(1 + cumulative_returns) - 1
max_drawdown = max(drawdowns)
```

There is no simple way to compute duration with array notation. Fortunately, this question shows how to use an accumulator for exactly your scenario:

```
from itertools import accumulate
drawdown_times = (drawdowns > 0).astype(np.int64)
max_drawdown_time = max(accumulate(drawdown_times, lambda x,y: (x+y)*y))
```

Alternatively, you can group the consecutive durations together. I don't recommend this approach, but I'll include it for posterity:

```
max_drawdown_time = drawdown_times.groupby((drawdown_times != drawdown_times.shift()).cumsum()).cumsum().max()
```

Correct answer by chrisaycock on March 2, 2021

In addition to chrisaycock's answer, we could also normalize the maximum drawdown with the high wartermarks instead of the current cumulative returns.

```
highwatermarks = cumulative_returns.cummax()
drawdowns = 1 - (1 + cumulative_returns) / (1 + highwatermarks)
max_drawdown = max(drawdowns)
```

Here we observe the drawdown definition defined here. $$MDD=frac{Trough Value−Peak Value}{Peak Value}$$

Answered by Kevin on March 2, 2021

Get help from others!

Recent Answers

- Jon Church on Why fry rice before boiling?
- Lex on Does Google Analytics track 404 page responses as valid page views?
- Peter Machado on Why fry rice before boiling?
- Joshua Engel on Why fry rice before boiling?
- haakon.io on Why fry rice before boiling?

Recent Questions

- How can I transform graph image into a tikzpicture LaTeX code?
- How Do I Get The Ifruit App Off Of Gta 5 / Grand Theft Auto 5
- Iv’e designed a space elevator using a series of lasers. do you know anybody i could submit the designs too that could manufacture the concept and put it to use
- Need help finding a book. Female OP protagonist, magic
- Why is the WWF pending games (“Your turn”) area replaced w/ a column of “Bonus & Reward”gift boxes?

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP