Measure how far an equity curve sits below its own running peak, and how long it stays there.
A drawdown is how far the equity curve sits below its own highest point so far. If one unit of money grew to 1.20 and then fell to 0.90, the drawdown is 0.90 / 1.20 - 1, which is -25%. The maximum drawdown is the worst of those numbers over the whole curve.
In this lesson I compute the drawdown of a six-point curve I can check by hand, then on AAPL: the deepest fall, the day it hit bottom, the peak it fell from, and how many trading days it took to get back to that peak.
Step 1. Six points
cummax() is the running maximum, in the same family as cumprod() from Lesson 21. Row 0 is the first value, row 1 is the larger of the first two, row 2 is the largest of the first three. It never goes down.
import pandas as pdequity = pd.Series([1.00, 1.20, 1.10, 0.90, 1.05, 1.30])peak = equity.cummax() # highest value up to and including this rowprint(peak)# -> 0 1.0# -> 1 1.2# -> 2 1.2# -> 3 1.2# -> 4 1.2# -> 5 1.3# -> dtype: float64
The curve reached 1.20 on row 1, so the peak stays at 1.20 through rows 2, 3 and 4 even while the curve falls. On row 5 the curve makes a new high and the peak follows it up.
The drawdown is the curve divided by that peak, minus 1.
Drawdown is zero on every row that sets a new high, and negative everywhere else. It is never positive, because the curve can never be above its own running maximum.
Row 3 by hand: the curve is at 0.90 and the peak behind it is 1.20.
Row 5 is a new high, so the curve ends with no drawdown at all, even though it lost a quarter of its value along the way.
Step 2. AAPL, buy and hold
prices.csv sits next to this lesson and holds simulated data: daily closes for AAPL, MSFT, NVDA and JNJ from 2020 to 2025. I take AAPL, turn the closes into returns, and compound them into an equity curve.
The worst point of the whole sample is 23 June 2023, where the curve was 29.82% below the best level it had reached before that day.
print((drawdown <0).sum(), "of", len(drawdown), "days below the peak")# -> 1472 of 1565 days below the peak
1472 of 1565 days below the peak
Step 3. From the peak, to the bottom, and back
Three dates describe the fall: the peak it started from, the trough, and the day the curve got back to the old peak. The peak date is the highest point of the curve up to the trough, which is .idxmax() on the slice ending at the trough.
The fall itself took 298 trading days. equity.loc[peak_date:trough_date] is the slice from one date to the other, and its length counts both ends, so I subtract one to count the steps between them.
For the recovery I take everything from the trough onwards and keep the rows that are back at the old peak level. The first of those is the day the drawdown closed.
NVDA fell 55.97% from its peak and JNJ fell 20.17%. Over the same six years NVDA returned 272.55% and JNJ returned 107.03%.
Your turn
Take NVDA. Find the peak date its worst drawdown started from, how many trading days the fall took, and whether the curve got back to that peak before the end of the sample.