σμ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 AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 7 · Pesquisa de alfa e risco›Tópico 7.7·~55 minAvançado

Testes de estresse

Posição no roteiroMódulo 7 de 11 · Tópico 7 de 9
7.7 Testes de estresse
—Nota do lab— / 100XP total—Sequência—

Perguntar «e se?» em vez de «qual a probabilidade?»

O VaR e o expected shortfall são estimados com retornos passados, então não veem o que nunca aconteceu. Um teste de estresse abandona as probabilidades e aplica um cenário escolhido: uma queda de 20% das ações, uma alta de 2 pontos nos juros, a crise de 2008 repetida, uma desvalorização. A perda da carteira nesse cenário é um número que um comitê de risco pode discutir e revela concentrações que uma média estatística esconde.

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

Uma tabela de cenários

As equipes de risco mantêm uma lista permanente de cenários, históricos (1987, 2008, março de 2020) e hipotéticos. Coloque-os numa matriz com uma linha por cenário e uma coluna por posição: a perda total em cada cenário é então um único produto matriz-vetor, scenarios @ values. Tomar o mínimo desse vetor nomeia o pior caso, e o cenário que mais machuca é em si uma informação, pois diz onde a carteira está exposta.

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%

O teste de estresse reverso

Inverta a pergunta: quão grave teria de ser um choque para nos custar um valor dado, como a reserva de capital? Tome uma direção de choques (digamos, tudo cai junto) e escale-a por um fator k. A perda é -k * (values @ direction), linear em k, então o fator que perde exatamente loss_limit é loss_limit / -(values @ direction). Se a direção não perde dinheiro, nenhum fator alcança o 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
Um cenário vale tanto quanto sua imaginaçãoO difícil é escolher cenários severos mas plausíveis, que movam juntos os ativos correlacionados, como numa crise. O resultado também é uma estimativa pontual sem probabilidade: um número ruim é motivo para olhar a posição, e um bom não prova nada sobre cenários em que não se pensou. Testes de estresse complementam o VaR; não o substituem.
  • Um teste de estresse aplica choques escolhidos: o pnl de uma posição é values * shocks, negativo em perdas e positivo para a venda a descoberto que ganha.
  • Vários cenários de uma vez são um produto, scenarios @ values; o mínimo nomeia o pior caso e mostra onde a carteira está exposta.
  • Estresse reverso: o múltiplo de um choque que perde exatamente o limite é loss_limit / -(values @ direction), ou infinito se a direção nunca perde.

A seguir: os modelos ARIMA, prever uma série a partir do seu próprio passado.

Lab: o que é avaliado

  • Atingir cada posição com shocked_pnl25 pts
  • Rodar vários cenários de uma vez com scenario_pnl25 pts
  • Nomear o pior caso com worst_scenario25 pts
  • Partir de uma perda com reverse_stress25 pts
Explorador · lab-99-1
▾ lab-99-1
…
bash · no navegador
learner@quantcraft:~/lab-99-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-99-1 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub