# 4. Useful Functions#

## 4.1. Find the maximum drawdown#

`PerformanceAnalytics`

provides several functions to calculate the drawdowns. What I offered here is an alternative to `PerformanceAnalytics::table.Drawdowns`

. The benefits of my solutoin are:

It’s

**1.89x**faster than`table.Drawdowns`

.It doesn’t require the user to convert the input into a

`zoo`

or`xts`

object, which is convenient if you’re using`data.table`

.

### 4.1.1. Create a Sample Data#

The only inputs of our `drawdowns`

function are 1) a return vector `ret`

and 2) a date vector `date`

. Note they should belong to the same stock.

Let’s generate these two variables.

```
set.seed(42)
# Generate 100 returns with mean 0 and standard deviation 0.05
ret = rnorm(100, mean=0, sd=0.05)
# Generate 100 dates starting from 2010/1/1
date = seq(as.Date("2010/1/1"), by="day", length.out=100)
```

### 4.1.2. The `drawdowns`

Function#

The `drawdowns`

below is a faster alternative to `PerformanceAnalytics`

.

**Inputs:**

`ret`

: a vector of returns`date`

: a vector of dates`top`

: the number of largest drawdowns to be returned

**Output:**
A `data.table`

with the following columns:

`start`

: the start date of the drawdown`trough`

: the date of the trough`end`

: the last day of the darwdown (if new watermark hasn’t been reached yet, it’s the last day in the sample).`depth`

: the drawdown (percentage).

```
library(data.table)
drawdowns <- function(ret, date, top=5) {
# This function calculates the drawdowns for a given return series
# Input:
# ret: a vector of returns
# date: a vector of dates
# top: the number of largest drawdowns to be returned
input = data.table(date, ret)[order(date)]
# a drawdown is defined whenever the cumulative return is below the previous peak
drawdowns_raw = input[, {
ret_cum=cumprod(1 + ret)
ret_cummax=cummax(c(1, ret_cum))[-1]
drawdowns=ret_cum/ret_cummax - 1
list(date, ret, ret_cum, ret_cummax, drawdowns)
}]
# print(drawdowns_raw)
# find the largest drawdowns
drawdowns = drawdowns_raw[, ':='(grp = rleid(drawdowns<0))
][drawdowns<0,
.(start=date[1], trough=date[which.min(drawdowns)],
end=date[.N], depth=drawdowns[which.min(drawdowns)]),
keyby=.(grp)
][order(depth)]
# return the top drawdowns
top = fifelse(top > nrow(drawdowns), nrow(drawdowns), top)
drawdowns = drawdowns[1:top][, ':='(grp=NULL)]
return(drawdowns[])
}
drawdowns(ret, date, top=2)
```

start | trough | end | depth |
---|---|---|---|

<date> | <date> | <date> | <dbl> |

2010-01-13 | 2010-02-16 | 2010-04-10 | -0.49650974 |

2010-01-02 | 2010-01-02 | 2010-01-03 | -0.02823491 |

If we use `PerformanceAnalytics::table.Drawdowns`

, the code and result are offered below. As you can see, the result is the same.

```
suppressMessages({library(PerformanceAnalytics)})
inputs = zoo(ret, order.by=date)
table.Drawdowns(inputs, top=2)[, c('From', 'Trough', 'Depth')]
```

From | Trough | Depth |
---|---|---|

<date> | <date> | <dbl> |

2010-01-13 | 2010-02-16 | -0.4965 |

2010-01-02 | 2010-01-02 | -0.0282 |