Visualizing Drawdown

Drawdown measures the decline of a time series variable from a historical peak. I explore visualizing and computing drawdown-based metrics.

In finance or economics, drawdown is a measure of the decline of a variable, say a stock’s return or a portfolio’s performance, from some historical peak. For example, Figure 11 is a time series of portfolio or strategy returns as a percentage, and the red line indicates the current drawdown.

Figure 1. Time series of cumulative returns (black). The current drawdown, assuming the right edge of the plot is "current", is the vertical distance from the largest historical peak and the current value (red line).

I have found that visualizing drawdowns a particular way is helpful for having the right intuition to compute various drawndown-related metrics.

High water mark

Imagine that our time series gets “flooded” in the sense that any trough gets filled with water if it drops below its current peak or high water mark. The trough is always full to the brim on its left edge, since we are working with time series data (Figure 22). The drawdown at any particular time point is simply the distance from the current variable value to the “surface of the water” or the high water mark at that time point.

Figure 2. Drawdown is the vertical distance from the variable's current value and the high water mark.

This immediately suggests a fairly simple way to compute the drawdown for all time points:

np.maximum.accumulate(x) - x

That’s it. We accumulate the maximum value so far to compute the high water mark, and the drawdown at each time point is simply the delta at that time point.

Deepest lake

To compute the maximum drawdown, we can accumulate the drawdown so far:

np.maximum.accumulate(
    np.maximum.accumulate(x) - x
)

We can visualize this by imagining that all the “lakes” in Figure 22 are at the same elevation. Then the maximum drawdown is the deepest lake (Figure 33).

Figure 3. The maximum drawdown can be visualized as the deepest lake after shifting all the "lakes" to the same high water mark.

Widest lake

Finally, computing the longest drawdown in duration, which is typically defined as the longest (worst) amount of time the variable of interest is lower than the historical peak, i.e. it is simply the “widest lake”. And the widest lake is simply the longest duration that the time series has been underwater (Figure 44).

Figure 4. The longest drawdown is the "widest lake" (thickest red line) out of all the "lakes" (thin red lines).

We can compute this by generating a logical array of time points that represent when the series x is “underwater”, and then calculating the longest sequence of True values:

uwater  = (x - cummax(x)) < 0
runs    = (~uwater).cumsum()[uwater]
max_dur = runs.value_counts(sort=True).iloc[0]

The above snippet is based on this StackOverflow answer. If we want to compute the start and end dates, we can do the following:

def calc_longest_drawdown(x):
    uwater  = (x - cummax(x)) < 0
    runs    = (~uwater).cumsum()[uwater]
    counts  = runs.value_counts(sort=True).iloc[:1]
    max_dur = counts.iloc[0]
    inds    = runs == counts.index[0]
    inds    = (inds).where(inds)
    start   = inds.first_valid_index()
    end     = inds.last_valid_index()
    return max_dur, start, end

This allows us to visualize the longest drawdown as a sanity check, as in Figure 44. Note that the above two snippets assume that x is a Pandas pd.Series with dates as an index.

Conclusion

For me, visualizing drawdown in this way has made it more intuitive to compute drawdown-based metrics. Every time a time series dips, it immediately forms a “lake”, and the drawdown is just the depth of the lake. The maximum drawdown is the deepest lake formed so far, and the longest drawdown is the widest lake so far.