σμ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.3·~45 minIntermédiaire

Queues épaisses et tests de normalité

Position dans le parcoursModule 3 sur 11 · Sujet 3 sur 12
3.3 Queues épaisses et tests de normalité
—Score du lab— / 100XP total—Série—

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 days

Les 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
Rejeter la normalité n'est pas la finAvec des centaines de rendements quotidiens, le test rejette la normalité pour presque tous les actifs : un rejet est donc attendu. Ce qui compte, c'est l'ampleur de l'écart des queues : utilisez le ratio de queue pour mesurer le problème, puis faites des tests de résistance avec de vrais krachs, une loi à queues plus lourdes comme la loi de Student, ou une simulation historique, plutôt que de vous fier à une courbe en cloche.
  • 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 est exp(-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
Explorateur · lab-56-1
▾ lab-56-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-56-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-56-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