Ce que promet la courbe en cloche
De nombreux modèles de risque supposent que les rendements suivent une loi normale, car elle rend tout calculable avec une moyenne et un écart type. La loi normale est très précise sur les extrêmes. La probabilité d'être à plus de k écarts types de la moyenne, dans un sens ou dans l'autre, vaut erfc(k / sqrt(2)) : 31,7 % pour 1, 4,55 % pour 2, 0,27 % pour 3, 0,0063 % pour 4. Une journée à 4 sigmas surviendrait alors environ une fois en 15 800 jours de bourse, soit à peu près une fois en 63 ans, et une journée à 5 sigmas une fois en plusieurs milliers d'années.
import math
import numpy as np
for k in (1, 2, 3, 4, 5):
probability = math.erfc(k / math.sqrt(2)) # P(|Z| > k) for a normal variable
print(k, f"{probability:.2e}")
# 1 3.17e-01
# 2 4.55e-02
# 3 2.70e-03
# 4 6.33e-05
# 5 5.73e-07
print(round(1 / math.erfc(4 / math.sqrt(2)))) # 15787 trading days between 4-sigma days, if returns were normal
print(round(1 / math.erfc(5 / math.sqrt(2)) / 252)) # 6922 years between 5-sigma daysLes marchés ont des queues épaisses
Les vrais marchés produisent des journées à 4 et 5 sigmas bien des fois dans une carrière, en 1987, 2008, 2020. Leurs distributions de rendements ont des queues épaisses : plus de journées extrêmes que la cloche ne le permet (et un centre plus haut et plus étroit). Une façon simple de le voir est le ratio de queue : la part observée de rendements au-delà de k écarts types divisée par la part qu'une loi normale donnerait. Un ratio proche de 1 signifie des queues normales ; 5, des extrêmes cinq fois trop fréquents ; pour des données quotidiennes à k = 4, il peut atteindre des centaines. Les modèles de risque qui supposent la normalité sous-estiment précisément les jours qui comptent le plus.
returns = np.array([0.010, 0.012, 0.008, 0.011, 0.009, 0.010, 0.012, -0.060, 0.010, 0.009])
def tail_ratio(x, k):
observed = np.mean(np.abs(x - x.mean()) > k * x.std(ddof=1)) # share beyond k standard deviations
return observed / math.erfc(k / math.sqrt(2)) # divided by what a normal would give
print(round(float(tail_ratio(returns, 2)), 2)) # 2.2 the one crash is 2.2 times too frequent at 2 sigma (a tiny sample, though)Le test de Jarque-Bera
Un test formel demande si l'échantillon peut raisonnablement provenir d'une loi normale. La statistique de Jarque-Bera combine les deux mesures de forme du sujet précédent : JB = n / 6 * (S² + K² / 4), avec S l'asymétrie et K l'aplatissement en excès. Pour un échantillon normal, les deux sont proches de 0 et JB est petit. Sous la normalité, JB suit une loi du khi-deux à 2 degrés de liberté, dont la queue a la forme fermée exp(-JB / 2) : c'est la p-valeur. Si elle est inférieure à 0,05, on rejette la normalité. La valeur critique est JB = 5,99. Notre échantillon de 10 jours avec un krach a JB = 22,4 et une p-valeur de 0,00001.
x = returns
n = len(x)
deviations = x - x.mean()
m2, m3, m4 = (np.mean(deviations ** k) for k in (2, 3, 4))
skew, kurt = m3 / m2 ** 1.5, m4 / m2 ** 2 - 3
jb = n / 6 * (skew ** 2 + kurt ** 2 / 4)
p_value = math.exp(-jb / 2)
print(round(float(jb), 2), p_value < 0.05) # 22.37 True normality rejected
print(f"{p_value:.1e}") # 1.4e-05# The p-value falls fast as the statistic grows: 5.99 is the 5% critical value
for jb in (0, 2, 5.99, 10, 22.37):
print(jb, round(math.exp(-jb / 2), 5))
# 0 1.0
# 2 0.36788
# 5.99 0.05004
# 10 0.00674
# 22.37 1e-05- Une variable normale est à plus de k écarts types avec une probabilité
erfc(k / sqrt(2)): 4,55 % pour 2, 0,0063 % pour 4. - Le ratio de queue, part observée divisée par la part normale, montre les queues épaisses : les marchés sont bien au-dessus de 1 à 4 sigmas.
- Jarque-Bera :
n / 6 * (S**2 + K**2 / 4); la p-valeur estexp(-JB / 2); sous 0,05, on rejette la normalité (JB au-dessus de 5,99).
À suivre : les tests d'hypothèse : le test t, pour décider si un rendement moyen est vraiment différent de zéro.
Lab : ce qui est noté
- Mesurer l'écart d'un échantillon à la normale avec
jarque_bera25 pts - Transformer la statistique en p-valeur avec
normality_p_value25 pts - Calculer ce que promet la courbe en cloche avec
normal_tail_probability25 pts - Compter de combien les queues sont plus épaisses avec
tail_ratio25 pts