σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 3 · Probabilités et simulation Monte-Carlo›Sujet 3.8·~40 minIntermédiaire

Les drawdowns

Position dans le parcoursModule 3 sur 11 · Sujet 8 sur 12
3.8 Les drawdowns
—Score du lab— / 100XP total—Série—

La perte que l'on ressent vraiment

La volatilité traite pareillement la hausse et la baisse, mais un investisseur vit avec un autre chiffre : de combien son compte est sous la meilleure valeur qu'il ait jamais eue. C'est le drawdown. Si un portefeuille est monté à 110 et vaut maintenant 98, il est à 10,9 % sous son sommet, quoi que dise la volatilité au jour le jour. Le drawdown maximal est le trou le plus profond de l'historique, et le temps sous l'eau est la durée qu'il a fallu pour en sortir. Les clients partent et les gestionnaires de risque réduisent les limites aux drawdowns, pas aux écarts types.

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]

Le trou le plus profond

cummax() donne le sommet courant en un appel. Diviser le prix par lui et soustraire 1 donne le drawdown de chaque jour, toujours nul ou négatif. Le drawdown maximal est simplement le minimum de cette série, et idxmin() dit quand il s'est produit. Les drawdowns sont asymétriques d'une façon qui compte : une perte de 20 % demande un gain de 25 % pour revenir, 1 / (1 - 0,20) - 1, et une perte de 50 % en demande 100 %. C'est pourquoi les gestionnaires de risque se battent pour garder les drawdowns peu profonds.

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

Le temps sous l'eau

La profondeur n'est que la moitié de l'histoire ; la durée en est l'autre moitié. Une stratégie peut ne perdre que 8 % et passer pourtant trois ans sous son ancien sommet, ce qui met à l'épreuve la patience de tout investisseur. Un jour est sous l'eau tant que son drawdown est négatif, et un nouveau sommet met fin à la période. Pour trouver la plus longue période avec pandas, marquez les jours sous l'eau par une comparaison, étiquetez chaque série de valeurs égales avec (flag != flag.shift()).cumsum(), puis comptez dans chaque série. Une simple boucle qui compte et remet à zéro fonctionne tout aussi bien et se lit facilement.

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
Un drawdown passé n'est pas le pireLe drawdown maximal d'un backtest est le pire survenu dans cet échantillon, pas le pire possible : un historique plus long contiendra presque toujours un trou plus profond. Considérez-le comme une borne inférieure, comparez-le au plus grand drawdown du marché sur la même période et gardez une marge pour dimensionner vos positions.
  • peak = prices.cummax() et drawdown = prices / peak - 1 : nul sur un nouveau sommet, négatif en dessous.
  • Le drawdown maximal est drawdown.min() ; une perte de x demande un gain de 1 / (1 - x) - 1 pour se rattraper.
  • Le temps sous l'eau est la plus longue série de jours à drawdown négatif ; le pire drawdown d'un backtest est une borne inférieure, pas une garantie.

À suivre : les générateurs de nombres aléatoires et les graines, le fondement de toute simulation.

Lab : ce qui est noté

  • Suivre le plus haut atteint avec running_peak25 pts
  • Mesurer la distance au sommet avec drawdown_series25 pts
  • Trouver la pire chute avec max_drawdown25 pts
  • Compter la plus longue attente d'un nouveau sommet avec longest_underwater25 pts
Explorateur · lab-61-1
▾ lab-61-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-61-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-61-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub