最大ドローダウンとは?下落の深さと回復期間
最大ドローダウンの意味と計算方法を解説します。ピークからボトムまでの下落の深さと、新高値に戻るまでの回復期間を確認できます。
最大ドローダウンとは、価値の推移におけるピークからボトムまでの最大下落幅を、直前のピークに対する割合で示したものである。12万ドルまで上昇した口座が8万4,000ドルまで下落し、その後回復した場合、最大ドローダウンは30%となる。この数値は、現在の状態ではなく、過去における最悪の下落局面を記録する統計であるため、その後も変わらない。多くの人は下落率だけを示して終える。しかし、この統計にはさらに二つの数値が含まれる。ピークから安値までの期間と、その安値から新高値を更新するまでの、はるかに長い回復期間である。
最大ドローダウンはどのように計算するのか
計算は系列を一度走査するだけで完了する。各観測時点で累積最大値を更新し、現在値をその最大値と比較し、それまでに記録した最悪値を保持する。
- 累積ピークを最初の値に設定する。
- 新しい値ごとに、その値が高ければピークを引き上げる。
- その時点のドローダウンを計算する。現在値をピークで割り、一を引く。
- 最もマイナス幅が大きい値を保持する。それが最大ドローダウンとなる。
収益率系列、ボラティリティ推定値、分布の仮定はいずれも必要ない。Python標準ライブラリを使い、単純な数値リストに対してループを実行する。新規のUbuntu環境では、まずインタープリターをインストールする。
apt-get update && apt-get install -y python3
このループをmaxdd.pyとして保存する。以下の価格は、手計算で確認できるように小さくした、作成上の十時点の系列である。
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}%")
python3 maxdd.pyを実行すると、maximum drawdown: -15.4%と表示される。手計算でも確認できる。安値の前のピークは104で、安値は88である。88を104で割ると0.846になる。その後、系列は史上最高値の106で終わるが、15.4%という数値は変わらない。最大ドローダウンは、サンプル期間中の最悪時点を恒久的に記録する指標であり、系列が現在どの位置にあるかは示さない。同じループに変数を二つ追加すれば、ピークを記録したインデックスと、最悪値を記録したインデックスという発生時点も取得できる。
ドローダウン曲線の見方
ドローダウン曲線は、各時点でステップ3を計算し、最悪値一つに集約しない。時系列が新高値を更新するたびに線はゼロに戻り、その間はゼロを下回る。このため、この乖離は「水面下」と呼ばれる。以下は、S&P 500に連動するETFであるSPYについて、2016年1月以降の月末終値から作成した曲線である。
各数値の背後にある正確なSQL
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_start127回の月末データを通じて、線は長期間ゼロ近辺に張り付く一方、何度か深いくぼみに落ち込む。記録上の最終月は、累積ピーク比で-1.27%となっている。この形状には、理解に役立つ特徴が二つある。回復は急反発ではなく、緩やかな上昇となる。新高値を更新するたびに曲線はゼロへ戻り、それ以前の動きが視覚的に消える。
ドローダウンから回復するまでの期間はどのくらいか
一つのドローダウンには、三つの異なる数値が含まれる。これらを一つの数値として扱うことが、パフォーマンス表で最も多い誤読である。
- 下落幅:ピークから何%下落したか。
- 安値までの期間:ピークから底値までの暦日数。
- 回復期間:底値から新高値に戻るまでの暦日数。
通常、最も長いのは三つ目である。見出しの数値だけでは、この期間は分からない。以下のパネルでは、日次終値を基に、2016年以降のSPYについて、完了したすべての含み損期間を再構成している。含み損期間はピークを付けた日に始まり、ファンドが初めてその水準を上回って引けた日に終わる。
各数値の背後にある正確なSQL
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 8一覧で最も深い完了済みの含み損期間はFeb 2020に始まり、直前のピークを34.2%下回った。下落に33暦日を要し、回復には148を要したため、合計181日間、水面下にあった。次のJan 2022の期間は、25.4%にとどまり、ファンドは746日間、過去の高値を下回った。
下落幅と期間は、完全に別のものだ。急落後に短期間で往復するケースも、浅い下落が数年続くケースも、一つのパーセンテージに集約される。2020年3月の急落は前者の典型例であり、急落から市場が回復する仕組みはその後半の動きを示している。
測定期間が長いほど最大ドローダウンが悪化する理由
最大ドローダウンは最大値であるため、過去のデータを追加すると、横ばいになるか、より深くなるだけです。長い期間の各時点における累積ピークは、短い期間のピーク以上になります。このため、算術的にそうした結果になります。2026年7月31日を終点とする五つの期間で同じファンドを測定すると、この影響が分かります。
各数値の背後にある正確なSQL
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_years直近1 year期間では、測定された最大下落率は9.1%でした。同じ系列を10 yearsまで遡って延長すると、数値は34.2%となり、最大下落時点はMar 2020でした。ファンドのリスクが高まったわけではありません。標本期間が長くなっただけです。
同じ算術的な性質は、運用戦略の比較も歪めます。三年間のバックテストと二十年間のバックテストは、同一の挙動を示していても、報告されるドローダウンは大きく異なる可能性があります。そのため、測定期間を併記しなければ、この数値の意味は限定的です。バックテストにおける先読みバイアスでは、見た目のよいバックテストが自らを実態以上に良く見せる別の方法を説明しています。
最大ドローダウンとボラティリティ
ボラティリティは、日々の値動きが上下いずれの方向にどの程度になるかを示す。最大ドローダウンは、特定の一つの下落経路を示す。いずれもリスク指標だが、答える問いは異なる。パネルでは、2026年7月31日までの五年間について、代表的な八銘柄を両指標で比較している。ボラティリティは日次終値から年率換算した。
各数値の背後にある正確なSQL
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 DESC二つの列を併せて確認したい。VZは、年率ボラティリティが22.6%で、グループ内で最も大きい45.4%の下落を記録した。下落が最も浅かったKOは、16.7%のボラティリティに対して20.9%下落した。ボラティリティは対象期間の全営業日における平均値であり、長く緩やかな下落は平均値に埋もれやすい。ドローダウンは、実際に価格がたどった最悪の一経路を示す。値動きが穏やかな保有銘柄でも、二年間にわたって下落し続ければ、ボラティリティが目立たない一方で、深いドローダウンを記録することがある。
低ボラティリティ・アノマリーは、値動きの穏やかな銘柄が長期的にどのように推移するかを検証する。Kelly基準によるポジションサイジングは、許容するドローダウンをポジションサイズへ変換する方法を示す。
数値だけでは分からないこと
押さえておくべき限界は四つあります。
- これは実現した一つの経路にすぎません。同じプロセスでも、別の結果になれば、はるかに悪い数値になり得ます。
- サンプル期間に左右されます。そのため、二つのドローダウンを比較するには、対象期間をそろえる必要があります。
- サンプリング頻度に左右されます。同じ期間でも、月末終値では日中の安値が見えないため、日次終値より小さい数値になります。
- 将来を示す情報は含まれません。ピークから30%下にあることだけでは、31番目の時点が明日訪れるかどうかは分かりません。
最後の限界があるため、自動売買システムにはドローダウンの閾値ではなく、ドローダウン・ポリシーが必要です。「20%で停止する」という設定は一つの問いに答えるだけで、二つの問いを残します。停止時に保有ポジションをどうするのか。システムを再稼働させる根拠は何か。トレーディングボットのサーキットブレーカーでは、このポリシーの構造を検討します。恣意的な一律ルールのコストは、回復局面で表れます。安値付近で停止し、再稼働が遅れたシステムは、上昇局面に参加できません。最良の日を逃すことでは、その影響を数値で示します。
二つの数値を比較する前に、重要な測定上の違いがあります。ファンドの価格系列に基づくドローダウンと、実際の口座のドローダウンは、現金の入出金がある場合や配当が入金される場合には異なります。この点は月次リターンの測定方法で詳しく説明しています。
最大ドローダウンに関するFAQ
適切な最大ドローダウンとは
一律の基準値はありません。比較対象となるベンチマークと、測定期間を定める必要があります。2026年7月31日までの十年間では、SPYの直近高値からの最大下落率は、日次終値ベースで34.2%でした。より短い期間で小さな数値を示す戦略が、必ずしも安全とは限りません。
最大ドローダウンはボラティリティと同じですか
いいえ。ボラティリティは通常の日々の値動きを示します。最大ドローダウンは、高値から安値までの経路のうち、単一の最悪局面を示します。値動きが小さい保有資産でも、二年間にわたり下落が続けば、新高値を更新し続ける値動きの大きい資産より深いドローダウンになることがあります。
最大ドローダウンから回復するには、どの程度の期間が必要ですか
ケースによって異なります。回復までの上昇局面が、全期間の長い部分を占めることも少なくありません。2016年以降のSPYでは、過去の高値を下回っていた期間のうち、最も長く完全に回復するまでに要した期間は、高値から新高値まで181暦日でした。そのうち33日は下落局面でした。
最大ドローダウンは将来の損失を予測しますか
いいえ。最大ドローダウンは、すでに発生した一つの値動きの経路を示すにすぎません。また、測定期間が長くなるほど、数値はほぼ必ず大きくなります。これは、戦略がこれまで耐えてきた下落を記録するものであり、今後の損失に上限を設けるものではありません。
上記の各パネルには、背後で使用したSQLが保存されています。いずれかを開き、ティッカーまたは期間を変更して、Strasmoreターミナルで同じドローダウン計算を実行できます。