σμ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 7 · Recherche d'alpha et risque›Sujet 7.4·~50 minAvancé

VaR historique

Position dans le parcoursModule 7 sur 11 · Sujet 4 sur 9
7.4 VaR historique
—Score du lab— / 100XP total—Série—

Une limite de perte avec un niveau de confiance

La Value at Risk répond : combien puis-je perdre un mauvais jour, mais pas le pire ? Une VaR à 1 jour à 95 % de 2 % dit que, 95 jours sur 100, la perte ne devrait pas dépasser 2 %. La méthode historique n'exige aucun modèle. On prend les rendements passés, on les trie et on lit la perte au 5e centile : la VaR est l'opposé de ce quantile. Régulateurs et limites de risque l'utilisent partout parce que c'est un seul nombre qu'un gérant peut comparer à une limite.

import numpy as np

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)           # two years of daily returns

var_95 = -np.quantile(returns, 0.05)                    # minus the 5th percentile
var_99 = -np.quantile(returns, 0.01)
print(round(float(var_95), 5), round(float(var_99), 5))  # 0.01928 0.027  the 99% loss limit is larger

Tester une VaR : les dépassements

Une VaR est une prévision, donc elle peut être vérifiée. Un dépassement est un jour où la perte a excédé la VaR, return < -VaR. Avec une VaR à 95 %, on attend environ 5 % de jours de dépassement : beaucoup plus signifie que le modèle sous-estime le risque, beaucoup moins qu'il est trop prudent. Les régulateurs comptent les dépassements sur 250 jours précisément pour cela. Notez l'inégalité stricte : une perte égale à la VaR n'est pas un dépassement.

import numpy as np

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)
var_95 = -np.quantile(returns, 0.05)

exceptions = int(np.sum(returns < -var_95))
print(exceptions, "exceptions in", len(returns), "days; expected about", int(0.05 * len(returns)))     # 25 25

Horizons plus longs, et comme cela s'est passé

Une VaR à 10 jours est souvent exigée. Avec des rendements quotidiens indépendants, la volatilité croît avec la racine du temps, donc la règle de la racine du temps met à l'échelle : VaR_T = VaR_1 * sqrt(T). C'est une approximation, plus faible avec des queues épaisses et des rendements autocorrélés. Les backtests honnêtes respectent aussi le temps : la VaR utilisée au jour t ne peut employer que les rendements avant le jour t, la même discipline que pour éviter l'anticipation. Une VaR glissante le fait avec une fenêtre des n derniers jours.

# The square-root-of-time rule for several horizons
import math

one_day = 0.0186
for days in (1, 5, 10, 20):
    print(days, round(one_day * math.sqrt(days), 4))        # 1: 0.0186, 5: 0.0416, 10: 0.0588, 20: 0.0832
import numpy as np

def historical_var(returns, confidence):
    return float(-np.quantile(returns, 1 - confidence))

def rolling_var(returns, window, confidence):
    out = np.full(len(returns), np.nan)
    for t in range(window, len(returns)):
        out[t] = historical_var(returns[t - window:t], confidence)      # the days BEFORE day t
    return out

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)
rolling = rolling_var(returns, 250, 0.99)
print(np.isnan(rolling[:250]).all(), round(float(rolling[250]), 5), round(float(rolling[-1]), 5))
exceptions = int(np.sum(returns[250:] < -rolling[250:]))
print(exceptions, "exceptions in", len(returns) - 250, "days (about 2 expected at 99%)")
print(round(historical_var(returns, 0.99) * np.sqrt(10), 5))                 # a 10-day figure by the square-root rule
Ce que la VaR ne dit pasLa VaR est un seuil, pas un pire cas : elle ne dit rien sur la gravité des pertes au-delà, c'est pourquoi les sujets suivants examinent l'expected shortfall et les tests de résistance. La méthode historique vaut ce que vaut sa fenêtre : deux années calmes donnent une petite VaR juste avant une crise, et le chiffre à 99 % ne repose que sur une poignée d'observations.
  • La VaR historique est l'opposé du quantile (1 - confidence) des rendements passés (np.quantile) : une fraction de perte positive sans modèle.
  • Testez en comptant les dépassements (return < -VaR) : environ 1 - confidence des jours ; davantage signifie une VaR trop basse.
  • Passez à un horizon avec var * sqrt(days) ; une fenêtre glissante n'utilise que les jours antérieurs ; la VaR donne un seuil, pas l'ampleur des pertes au-delà.

À suivre : la VaR paramétrique, calculer le même seuil à partir d'une loi normale plutôt que de l'historique brut.

Lab : ce qui est noté

  • Lire la perte dans l'historique avec historical_var25 pts
  • Compter les dépassements avec var_exceptions25 pts
  • Allonger l'horizon avec scale_var25 pts
  • L'estimer comme elle s'est produite avec rolling_var25 pts
Explorateur · lab-96-1
▾ lab-96-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-96-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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