実際に感じる損失
ボラティリティは上昇と下落を同じに扱いますが、投資家が向き合うのは別の数字、口座がこれまでの最高値からどれだけ下にあるかです。これがドローダウンです。ポートフォリオが110まで上がって今98なら、日々のボラティリティが何と言おうと、高値から10.9%下にいます。最大ドローダウンは履歴で最も深い穴で、水面下にいた期間はそこから抜け出すのにかかった長さです。顧客が去り、リスク管理者が限度を引き下げるのは、標準偏差ではなくドローダウンによってです。
import pandas as pd
prices = pd.Series(
[100, 105, 103, 98, 104, 110, 108, 107, 111, 95], dtype=float,
index=pd.bdate_range("2026-03-02", periods=10),
)
peak = prices.cummax() # the highest price so far, day by day
drawdown = prices / peak - 1 # 0 on a new high, negative below it
print(peak.tolist()) # [100.0, 105.0, 105.0, 105.0, 105.0, 110.0, 110.0, 110.0, 111.0, 111.0]
print(drawdown.round(4).tolist()) # [0.0, 0.0, -0.019, -0.0667, -0.0095, 0.0, -0.0182, -0.0273, 0.0, -0.1441]最も深い穴
cummax() は累積最高値を1回の呼び出しで返します。価格をそれで割って1を引けば、毎日のドローダウン(常に0以下)になります。最大ドローダウンはこの系列の最小値で、idxmin() でいつ起きたかがわかります。ドローダウンには重要な非対称性があります。20%の損失を取り戻すには25%の利益(1 / (1 - 0.20) - 1)が必要で、50%の損失には100%が必要です。リスク管理者がドローダウンを浅く保とうと戦う理由です。
print(round(drawdown.min(), 4), drawdown.idxmin().date()) # -0.1441 2026-03-13 111 down to 95
# The climb back is steeper than the fall
for loss in (0.10, 0.20, 0.50):
print(loss, round(1 / (1 - loss) - 1, 3))
# 0.1 0.111
# 0.2 0.25
# 0.5 1.0水面下の期間
深さは話の半分で、期間がもう半分です。損失がわずか8%でも、以前の高値を下回ったまま3年過ごす戦略もあり、どんな投資家の忍耐も試されます。ドローダウンが負の間、その日は水面下にあり、新高値で区間が終わります。pandas で最長の区間を求めるには、比較で水面下の日に印を付け、(flag != flag.shift()).cumsum() で同じ値の連続ごとに番号を振り、各連続の中で数えます。数えてリセットする単純なループでも同じようにでき、読みやすくもあります。
underwater = drawdown < 0 # True while below the running peak
run_id = (underwater != underwater.shift()).cumsum() # a new id each time the flag changes
streak = underwater.astype(int).groupby(run_id).cumsum() # 1, 2, 3, ... inside each underwater run
print(streak.tolist()) # [0, 0, 1, 2, 3, 0, 1, 2, 0, 1]
print(int(streak.max())) # 3 the longest wait for a new high, in days# Return divided by the depth of the worst hole: the Calmar ratio
annual_return = 0.12
print(round(annual_return / abs(drawdown.min()), 2)) # 0.83過去のドローダウンが最悪とは限らないバックテストの最大ドローダウンは、その標本で起きた最悪であって、起こりうる最悪ではありません。履歴が長ければほぼ必ずもっと深い穴が含まれます。下限と考え、同じ期間の市場の最大ドローダウンと比べ、ポジションサイズには余裕を持たせましょう。
peak = prices.cummax()とdrawdown = prices / peak - 1:新高値で0、それ以外は負- 最大ドローダウンは
drawdown.min()。損失xの回復には1 / (1 - x) - 1の利益が必要 - 水面下の期間はドローダウンが負の日の最長の連続。バックテストの最悪ドローダウンは下限であって保証ではない
次は乱数生成器とシードです。あらゆるシミュレーションの土台となります。
ラボ:採点される内容
running_peakでここまでの最高値を追う25点drawdown_seriesで高値からの距離を測る25点max_drawdownで最悪の下落を見つける25点longest_underwaterで新高値を待つ最長期間を数える25点