σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 3 · 確率とモンテカルロ・シミュレーション›トピック 3.8·~40 分中級

ドローダウン

ロードマップ上の位置モジュール 3/11 · トピック 8/12
3.8 ドローダウン
—ラボスコア— / 100合計XP—連続日数—

実際に感じる損失

ボラティリティは上昇と下落を同じに扱いますが、投資家が向き合うのは別の数字、口座がこれまでの最高値からどれだけ下にあるかです。これがドローダウンです。ポートフォリオが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点
エクスプローラー · lab-61-1
▾ lab-61-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-61-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-61-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける