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.0Uma 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 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