σμ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.7·~55 minAvancé

Tests de résistance

Position dans le parcoursModule 7 sur 11 · Sujet 7 sur 9
7.7 Tests de résistance
—Score du lab— / 100XP total—Série—

Demander « et si ? » plutôt que « quelle probabilité ? »

La VaR et l'expected shortfall sont estimées à partir des rendements passés, donc elles ne voient pas ce qui ne s'est jamais produit. Un test de résistance abandonne les probabilités et applique un scénario que l'on choisit : un krach de 20 % des actions, une hausse de 2 points des taux, la crise de 2008 rejouée, une dévaluation. La perte du portefeuille dans ce scénario est un chiffre dont un comité des risques peut discuter, et elle révèle des concentrations qu'une moyenne statistique cache.

import numpy as np

values = np.array([500000.0, 300000.0, -200000.0])       # two longs and a short
shocks = np.array([-0.20, -0.10, 0.15])                  # one scenario: stocks -20%, bonds -10%, the shorted stock +15%

pnl = values * shocks                                    # per position
print(pnl)                                               # [-100000.  -30000.  -30000.]  the short loses when its stock rises
print(float(pnl.sum()))                                  # -160000.0

Un tableau de scénarios

Les équipes de risque tiennent une liste permanente de scénarios, historiques (1987, 2008, mars 2020) et hypothétiques. Placez-les dans une matrice avec une ligne par scénario et une colonne par position : la perte totale sous chaque scénario est alors un seul produit matrice-vecteur, scenarios @ values. Prendre le minimum de ce vecteur désigne le pire cas, et le scénario qui fait le plus mal est en soi une information, car il dit où le portefeuille est exposé.

import numpy as np

values = np.array([500000.0, 300000.0, -200000.0])
scenarios = np.array([[-0.20, -0.10,  0.15],             # equity crash
                      [-0.05,  0.02,  0.00],             # mild dip
                      [ 0.10,  0.05, -0.20]])            # the shorted stock falls 20%: the short gains
names = ["equity crash", "mild dip", "rally and short gain"]

pnl = scenarios @ values
for name, p in zip(names, pnl):
    print(name, round(float(p)))
worst = int(np.argmin(pnl))
print("worst:", names[worst], round(float(pnl[worst])))
# A shock does not have to be a price move: a rate jump hits a bond through its duration
duration = 7.5                                    # years, from the duration topic
bond_value = 1_000_000.0
rate_jump = 0.02                                  # +2 percentage points
print(round(bond_value * (-duration * rate_jump)))   # -150000   the shock expressed as a price return of -15%

Le test de résistance inversé

Inversons la question : quelle gravité de choc nous coûterait un montant donné, par exemple le coussin de capital ? Prenons une direction de chocs (par exemple, tout baisse ensemble) et multiplions-la par un facteur k. La perte est -k * (values @ direction), linéaire en k, donc le facteur qui perd exactement loss_limit est loss_limit / -(values @ direction). Si la direction ne fait pas perdre d'argent, aucun facteur n'atteint jamais la limite.

import numpy as np

def reverse_stress(values, direction, loss_limit):
    change = float(values @ direction)
    if change >= 0:
        return float("inf")
    return loss_limit / -change

values = np.array([500000.0, 300000.0, -200000.0])
direction = np.array([-1.0, -1.0, 0.0])                    # the two longs fall by the same percentage
k = reverse_stress(values, direction, 100000.0)
print(round(k, 4))                                          # 0.125  a 12.5% fall in both longs loses 100,000
print(round(float(-(values @ (k * direction))), 2))         # 100000.0
Un scénario ne vaut que par votre imaginationLa partie difficile est de choisir des scénarios sévères mais plausibles, qui font bouger ensemble les actifs corrélés, comme dans une crise. Le résultat est aussi une estimation ponctuelle sans probabilité : un mauvais chiffre est une raison d'examiner la position, un bon ne prouve rien pour les scénarios auxquels on n'a pas pensé. Les tests de résistance complètent la VaR ; ils ne la remplacent pas.
  • Un test de résistance applique des chocs choisis : le pnl d'une position est values * shocks, négatif pour les pertes, positif pour la vente à découvert qui gagne.
  • Plusieurs scénarios à la fois, c'est un produit, scenarios @ values ; le minimum désigne le pire cas et montre où le portefeuille est exposé.
  • Test inversé : le multiple d'un choc qui perd exactement la limite est loss_limit / -(values @ direction), ou l'infini si la direction ne perd jamais.

À suivre : les modèles ARIMA, prévoir une série à partir de son propre passé.

Lab : ce qui est noté

  • Frapper chaque position avec shocked_pnl25 pts
  • Lancer plusieurs scénarios d'un coup avec scenario_pnl25 pts
  • Nommer le pire cas avec worst_scenario25 pts
  • Remonter d'une perte avec reverse_stress25 pts
Explorateur · lab-99-1
▾ lab-99-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-99-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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