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.0Le 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.83peak = prices.cummax()etdrawdown = prices / peak - 1: nul sur un nouveau sommet, négatif en dessous.- Le drawdown maximal est
drawdown.min(); une perte dexdemande un gain de1 / (1 - x) - 1pour 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