Maximum drawdown: omvang versus hersteltijd
Maximum drawdown is de grootste daling van piek naar dieptepunt in een waardereeks. Lees wat een diepe daling is en waarom herstel naar een nieuwe recordhoogte langer duurt.
Maximum drawdown is de grootste daling van een piek naar een dieptepunt in een waardereeks, gemeten als percentage van de hoogste waarde tot dan toe. Een rekening die oploopt tot $120.000, daalt naar $84.000 en zich daarna herstelt, heeft een maximum drawdown van 30%. Dat percentage blijft voor altijd gelijk, omdat de statistiek de slechtste periode in de historie vastlegt en niet de huidige toestand weergeeft. De meeste mensen noemen alleen de omvang van de daling. Binnen dezelfde statistiek zitten nog twee andere cijfers: de tijd van de piek naar het dieptepunt en de veel langere tijd van dat dieptepunt naar een nieuwe recordhoogte.
Hoe wordt maximum drawdown berekend?
De volledige berekening bestaat uit één doorloop van de reeks. Bij elke observatie werkt u het voortschrijdende maximum bij, vergelijkt u de huidige waarde daarmee en bewaart u de slechtste uitkomst tot dan toe.
- Stel het voortschrijdende maximum in op de eerste waarde.
- Verhoog het maximum bij elke nieuwe waarde als die hoger is.
- Bereken de drawdown op dat moment: de waarde gedeeld door het maximum, minus één.
- Bewaar de meest negatieve uitkomst. Dat is de maximum drawdown.
Er komt geen returnreeks, volatiliteitsschatting of verdelingsaanname aan te pas. De lus draait op een eenvoudige lijst met getallen en gebruikt alleen de Python standard library. Installeer op een nieuwe Ubuntu-installatie eerst de interpreter:
apt-get update && apt-get install -y python3
Sla de lus op als maxdd.py. De onderstaande prijzen vormen een kunstmatige reeks van tien punten. De reeks is klein genoeg om handmatig te controleren:
prices = [100.0, 104.0, 98.0, 92.0, 95.0, 88.0, 90.0, 101.0, 99.0, 106.0]
peak = prices[0]
max_dd = 0.0
for price in prices:
if price > peak:
peak = price
drawdown = price / peak - 1.0
if drawdown < max_dd:
max_dd = drawdown
print(f"maximum drawdown: {max_dd * 100:.1f}%")
Voer python3 maxdd.py uit. De uitvoer is maximum drawdown: -15.4%. Controleer dit handmatig: het maximum vóór het dieptepunt is 104, het dieptepunt is 88 en 88 gedeeld door 104 is 0,846. De reeks eindigt vervolgens op 106, een all time high, maar de drawdown van 15,4% blijft de hoogste geregistreerde drawdown. Maximum drawdown is een permanent record van het slechtste moment in een steekproef. De maatstaf zegt niets over waar de reeks vandaag staat. Met twee extra variabelen in dezelfde lus legt u ook de timing vast: de index waarop het maximum werd bereikt en de index waarop de slechtste uitkomst ontstond.
Hoe een drawdowncurve eruitziet
Een drawdowncurve houdt stap 3 op elk moment bij in plaats van die terug te brengen tot één slechtste waarde. De lijn staat op nul wanneer de reeks een nieuwe high neerzet en blijft tussen die highs onder nul. Daarom wordt die zone ook wel ‘underwater’ genoemd. Hieronder staat de curve voor SPY, een exchange traded fund dat de S&P 500 volgt, op basis van slotstanden aan het einde van elke maand sinds januari 2016.
De exacte SQL achter elk getal
WITH monthly AS (
SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2016-01-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY month_start
),
runs AS (
SELECT month_start,
close_px,
max(close_px) OVER (ORDER BY month_start
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM monthly
)
SELECT formatDateTime(month_start, '%Y-%m') AS month,
round(100 * (close_px / running_peak - 1), 2) AS drawdown_pct
FROM runs
ORDER BY month_startOver 127 metingen aan het einde van de maand blijft de lijn lange perioden dicht bij nul en zakt zij op enkele momenten weg in diepe dalen. De laatste beschikbare maand staat op -1.27% ten opzichte van de oplopende piek. De vorm laat twee kenmerken duidelijk zien. Herstel verloopt via geleidelijke stijgingen en niet via scherpe rebounds. Elke nieuwe piek brengt de curve terug naar nul en wist de visuele herinnering aan de voorafgaande drawdown.
Hoe lang duurt het om van een drawdown te herstellen?
In één drawdown zitten drie afzonderlijke cijfers. Deze worden vaak ten onrechte als één cijfer gelezen in een performance-overzicht:
- Diepte: hoe ver de waarde in procenten onder de piek is gedaald.
- Tijd tot het dieptepunt: het aantal kalenderdagen vanaf de piek tot de bodem.
- Hersteltijd: het aantal kalenderdagen vanaf die bodem tot een nieuwe hoogste stand.
De derde periode is meestal de langste. Een headlinecijfer laat deze periode nooit zien. Het onderstaande paneel reconstrueert elke voltooide periode onder de vorige piek voor SPY sinds 2016 op basis van dagelijkse slotkoersen. Een periode begint op de dag waarop een piek wordt bereikt en eindigt op de dag waarop het fonds voor het eerst boven die piek sluit.
De exacte SQL achter elk getal
WITH daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2016-01-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY session_date
),
runs AS (
SELECT session_date,
close_px,
max(close_px) OVER (ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM daily
),
episodes AS (
SELECT running_peak AS peak_px,
min(session_date) AS peak_date,
argMin(session_date, close_px) AS trough_date,
min(close_px) AS trough_px
FROM runs
GROUP BY running_peak
),
sequenced AS (
SELECT peak_px,
peak_date,
trough_date,
trough_px,
leadInFrame(peak_date, 1) OVER (ORDER BY peak_date
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS recovery_date
FROM episodes
)
SELECT formatDateTime(peak_date, '%b %Y') AS episode_label,
round(100 * (1 - trough_px / peak_px), 1) AS fall_from_peak_pct,
dateDiff('day', peak_date, trough_date) AS days_to_trough,
dateDiff('day', trough_date, recovery_date) AS days_to_new_high,
dateDiff('day', peak_date, recovery_date) AS days_underwater
FROM sequenced
WHERE recovery_date > trough_date
AND round(100 * (1 - trough_px / peak_px), 1) >= 5
ORDER BY fall_from_peak_pct DESC
LIMIT 8De diepste voltooide periode in de lijst begint in Feb 2020 en bereikt een niveau van 34.2% onder de vorige piek. De daling duurde 33 kalenderdagen. Het herstel nam 148 dagen in beslag. In totaal stond het fonds 181 dagen onder de vorige piek. De volgende periode, uit Jan 2022, is met 25.4% minder diep. Het fonds bleef daarbij 746 dagen onder de oude hoogste stand.
Diepte en duur kunnen volledig los van elkaar staan. Een scherpe daling met een snel herstel en een geleidelijke daling die jaren aanhoudt, worden in één percentage samengevat. De crash van maart 2020 is het schoolvoorbeeld van het eerste patroon. Hoe markten herstellen van crashes beschrijft het tweede deel van de beweging.
Waarom de maximale drawdown groter wordt naarmate u langer meet
Maximum drawdown is een maximum. Meer historie kan de uitkomst daarom alleen gelijk laten of dieper maken. De voortschrijdende piek in een langere meetperiode is op elk moment minstens even hoog als de piek in een kortere periode. Dat bepaalt de uitkomst rekenkundig. Als u hetzelfde fonds meet over vijf periodes die allemaal eindigen op 31 juli 2026, wordt dit effect zichtbaar.
De exacte SQL achter elk getal
WITH daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2016-07-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY session_date
),
scoped AS (
SELECT arrayJoin([1, 2, 3, 5, 10]) AS lookback_years,
session_date,
close_px
FROM daily
),
windowed AS (
SELECT lookback_years, session_date, close_px
FROM scoped
WHERE session_date >= subtractYears(toDate('2026-07-31'), lookback_years)
),
runs AS (
SELECT lookback_years,
session_date,
close_px,
max(close_px) OVER (PARTITION BY lookback_years ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM windowed
)
SELECT concat(toString(lookback_years), if(lookback_years = 1, ' year', ' years')) AS lookback,
round(100 * max(1 - close_px / running_peak), 1) AS max_drawdown_pct,
formatDateTime(argMax(session_date, 1 - close_px / running_peak), '%b %Y') AS worst_point
FROM runs
GROUP BY lookback_years
ORDER BY lookback_yearsOver de trailing 1 year-periode bedroeg de grootste daling 9.1%. Als u dezelfde reeks uitbreidt naar 10 years, komt de uitkomst uit op 34.2%. Het dieptepunt viel in Mar 2020. Het fonds is niet risicovoller geworden. De meetperiode is langer geworden.
Dezelfde rekenkundige eigenschap vertekent ook vergelijkingen tussen strategieën. Een backtest over drie jaar en een backtest over twintig jaar kunnen identiek gedrag beschrijven en toch zeer verschillende drawdowns rapporteren. Het cijfer zegt daarom weinig zonder de bijbehorende meetperiode. Look-ahead bias in backtesting behandelt een andere manier waarop een op het eerste gezicht overtuigende backtest zichzelf gunstiger kan voorstellen.
Maximale drawdown versus volatiliteit
Volatiliteit meet de gebruikelijke omvang van een dagelijkse beweging in beide richtingen. De maximale drawdown meet één specifiek neerwaarts koersverloop. Beide zijn risicomaatstaven, maar ze beantwoorden verschillende vragen. Het paneel zet beide naast elkaar voor acht bekende namen over de vijf jaar tot en met 31 juli 2026. De volatiliteit is geannualiseerd op basis van dagelijkse slotkoersen.
De exacte SQL achter elk getal
WITH daily AS (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'AAPL', 'MSFT', 'KO', 'JNJ', 'CVX', 'VZ', 'PG')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2021-08-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, session_date
),
runs AS (
SELECT ticker,
session_date,
close_px,
max(close_px) OVER (PARTITION BY ticker ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak,
lagInFrame(close_px, 1) OVER (PARTITION BY ticker ORDER BY session_date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
FROM daily
)
SELECT ticker,
round(100 * max(1 - close_px / running_peak), 1) AS max_drawdown_pct,
round(100 * sqrt(252) * stddevSampIf(close_px / prev_close - 1, prev_close > 0), 1) AS annualized_volatility_pct
FROM runs
GROUP BY ticker
HAVING countIf(prev_close > 0) > 20
ORDER BY max_drawdown_pct DESCLees de twee kolommen samen. VZ noteert de grootste daling van de groep, met 45.4%, tegenover een geannualiseerde volatiliteit van 22.6%. Aan de andere kant daalde KO met 20.9%, bij een volatiliteit van 16.7%. Volatiliteit is een gemiddelde over elke dag in de periode. Gemiddelden zwakken een langdurige, geleidelijke daling af. Drawdown is het slechtste koersverloop dat de prijs daadwerkelijk heeft afgelegd. Een rustige positie die twee jaar lang geleidelijk daalt, kan een diepe drawdown laten zien bij een onopvallende volatiliteit.
De anomalie van lage volatiliteit kijkt naar het gedrag van rustige namen over langere perioden. Position sizing volgens het Kelly-criterium laat zien hoe een drawdown-tolerantie wordt vertaald naar een positiegrootte.
Wat het cijfer u niet kan vertellen
Vier beperkingen zijn belangrijk:
- Het is één gerealiseerd verloop. Hetzelfde proces had bij een andere trekking een veel slechter cijfer kunnen opleveren.
- Het hangt af van de lengte van de steekproef. Twee drawdowns zijn alleen vergelijkbaar over overeenkomende perioden.
- Het hangt af van de meetfrequentie. Slotkoersen aan het einde van de maand verbergen de intraday-dieptepunten en leveren over dezelfde periode een kleiner cijfer op dan dagelijkse slotkoersen.
- Het bevat geen informatie over de toekomst. Dat een niveau 30% onder een piek ligt, zegt niets over de vraag of het 31e meetpunt morgen wordt bereikt.
Die laatste beperking verklaart waarom een geautomatiseerd systeem een drawdownbeleid nodig heeft en niet alleen een drawdownthreshold. ‘Stop bij 20%’ beantwoordt één vraag en laat er twee onbeantwoord: wat gebeurt er bij de stop met open posities, en welk bewijs is nodig om het systeem weer in te schakelen. Circuitbreakers voor tradingbots werkt de opzet van dat beleid uit. De kosten van een willekeurige vaste regel worden zichtbaar tijdens het herstel. Een systeem dat dicht bij het dieptepunt stopt en pas laat opnieuw start, mist de stijging. De beste dagen missen zet die berekening in cijfers uiteen.
Voordat u twee cijfers vergelijkt, is één meetdetail belangrijk: een drawdown op de koersreeks van een fonds verschilt van een drawdown op een echte rekening wanneer er cash in- of uitstroomt en wanneer dividenden worden uitgekeerd. Hoe maandrendementen worden gemeten behandelt dit volledig.
FAQ over maximum drawdown
Wat is een goede maximum drawdown?
Er bestaat geen universele waarde. U hebt alleen een benchmark en een meetperiode nodig. In de tien jaar tot en met 31 juli 2026 bedroeg de grootste daling van SPY vanaf een tussentijdse piek, gemeten op basis van dagelijkse slotkoersen, 34.2%. Een strategie die over een kortere periode een kleiner getal rapporteert, is daarom niet per se veiliger.
Is maximum drawdown hetzelfde als volatiliteit?
Nee. Volatiliteit beschrijft de gebruikelijke dagelijkse beweging. Maximum drawdown beschrijft het slechtste traject van piek tot bodem. Een positie met lage volatiliteit die twee jaar lang geleidelijk daalt, kan een grotere drawdown hebben dan een beweeglijke positie die steeds nieuwe recordhoogtes neerzet.
Hoe lang duurt het herstel van een maximum drawdown?
Dat verschilt. Het herstel duurt vaak langer dan de daling. Voor SPY sinds 2016 duurde de langste volledig herstelde periode onder een eerdere recordhoogte 181 kalenderdagen, gerekend van de piek tot een nieuwe recordhoogte. Daarvan werd 33 dagen dalend doorgebracht.
Voorspelt maximum drawdown toekomstige verliezen?
Nee. De maatstaf beschrijft één traject dat al heeft plaatsgevonden. Een langere meetperiode leidt vrijwel altijd tot een groter getal. Maximum drawdown laat zien wat een strategie tot nu toe heeft doorstaan. Er is geen bovengrens voor wat daarna kan gebeuren.
Elk hierboven weergegeven paneel bevat de onderliggende SQL-query. Open een paneel, vervang de ticker of de meetperiode en voer dezelfde drawdown-berekening uit op de Strasmore-terminal.