σμ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 AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 7 · Investigación de alfa y riesgo›Tema 7.7·~55 minAvanzado

Pruebas de estrés

Posición en la rutaMódulo 7 de 11 · Tema 7 de 9
7.7 Pruebas de estrés
—Puntuación del lab— / 100XP total—Racha—

Preguntar «¿y si?» en vez de «¿qué probabilidad?»

El VaR y el expected shortfall se estiman con rendimientos pasados, así que no ven lo que nunca ha ocurrido. Una prueba de estrés abandona las probabilidades y aplica un escenario elegido: una caída del 20 % de las acciones, una subida de 2 puntos de los tipos, la crisis de 2008 repetida, una devaluación. La pérdida de la cartera en ese escenario es un número que un comité de riesgos puede discutir y revela concentraciones que un promedio estadístico oculta.

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

Una tabla de escenarios

Los equipos de riesgos mantienen una lista permanente de escenarios, históricos (1987, 2008, marzo de 2020) e hipotéticos. Colócalos en una matriz con una fila por escenario y una columna por posición: la pérdida total en cada escenario es entonces un único producto matriz-vector, scenarios @ values. Tomar el mínimo de ese vector nombra el peor caso, y el escenario que más daña es en sí información, pues dice dónde está expuesta la cartera.

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%

La prueba de estrés inversa

Invirtamos la pregunta: ¿cuán grave tendría que ser un shock para costarnos una cantidad dada, como el colchón de capital? Tomemos una dirección de shocks (por ejemplo, todo cae a la vez) y escalémosla por un factor k. La pérdida es -k * (values @ direction), lineal en k, así que el factor que pierde exactamente loss_limit es loss_limit / -(values @ direction). Si la dirección no pierde dinero, ningún factor alcanza el límite.

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 escenario vale lo que valga tu imaginaciónLo difícil es elegir escenarios severos pero plausibles, que muevan juntos los activos correlacionados, como en una crisis. El resultado es además una estimación puntual sin probabilidad asociada: un mal número es motivo para revisar la posición, y uno bueno no prueba nada sobre los escenarios en que no se pensó. Las pruebas de estrés complementan el VaR; no lo sustituyen.
  • Una prueba de estrés aplica shocks elegidos: el pnl de una posición es values * shocks, negativo en pérdidas y positivo para el corto que gana.
  • Varios escenarios a la vez son un producto, scenarios @ values; el mínimo nombra el peor caso y muestra dónde está expuesta la cartera.
  • Estrés inverso: el múltiplo de un shock que pierde exactamente el límite es loss_limit / -(values @ direction), o infinito si la dirección nunca pierde.

A continuación: los modelos ARIMA, pronosticar una serie a partir de su propio pasado.

Lab: qué se califica

  • Golpear cada posición con shocked_pnl25 pts
  • Ejecutar varios escenarios a la vez con scenario_pnl25 pts
  • Nombrar el peor caso con worst_scenario25 pts
  • Partir de una pérdida con reverse_stress25 pts
Explorador · lab-99-1
▾ lab-99-1
…
bash · en el navegador
learner@quantcraft:~/lab-99-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-99-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub