„Was wäre, wenn?“ statt „wie wahrscheinlich?“ fragen
VaR und Expected Shortfall werden aus vergangenen Renditen geschätzt und sehen daher nicht, was nie passiert ist. Ein Stresstest lässt die Wahrscheinlichkeiten weg und wendet ein selbst gewähltes Szenario an: einen Aktiencrash um 20 %, einen Zinssprung um 2 Punkte, die Krise von 2008 im Replay, eine Abwertung. Der Verlust des Portfolios in diesem Szenario ist eine Zahl, die ein Risikokomitee diskutieren kann, und er zeigt Konzentrationen, die ein statistischer Mittelwert verbirgt.
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.0Eine Tabelle von Szenarien
Risikoteams führen eine feste Liste von Szenarien, historische (1987, 2008, März 2020) und hypothetische. Legt man sie in eine Matrix mit einer Zeile je Szenario und einer Spalte je Position, ist der Gesamtverlust unter jedem Szenario ein einziges Matrix-Vektor-Produkt, scenarios @ values. Das Minimum dieses Vektors benennt den schlimmsten Fall, und das Szenario, das am meisten schmerzt, ist selbst eine Information, denn es zeigt, wo das Portfolio exponiert ist.
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%Der umgekehrte Stresstest
Man dreht die Frage um: Wie schlimm müsste ein Schock sein, um einen gegebenen Betrag zu kosten, etwa den Kapitalpuffer? Man nimmt eine Schockrichtung (etwa alles fällt gemeinsam) und skaliert sie mit einem Faktor k. Der Verlust ist -k * (values @ direction), linear in k, der Faktor mit genau loss_limit Verlust ist also loss_limit / -(values @ direction). Verliert die Richtung kein Geld, erreicht kein Faktor je die Grenze.
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- Ein Stresstest wendet gewählte Schocks an: Der PnL einer Position ist
values * shocks, negativ bei Verlusten und positiv für den Short, der gewinnt. - Viele Szenarien auf einmal sind ein Produkt,
scenarios @ values; das Minimum benennt den schlimmsten Fall und zeigt, wo das Portfolio exponiert ist. - Umgekehrter Stress: Das Vielfache eines Schocks, das genau das Limit verliert, ist
loss_limit / -(values @ direction), oder unendlich, wenn die Richtung nie verliert.
Als Nächstes: ARIMA-Modelle, eine Reihe aus ihrer eigenen Vergangenheit vorhersagen.
Lab: Was bewertet wird
- Mit
shocked_pnljede Position treffen25 Pkt. - Mit
scenario_pnlviele Szenarien auf einmal rechnen25 Pkt. - Mit
worst_scenarioden schlimmsten Fall benennen25 Pkt. - Mit
reverse_stressvon einem Verlust zurückrechnen25 Pkt.