Define a joint shock and decide how to reprice
- Record positions, current prices, currency exposures and valuation date. Choose the scenario horizon before selecting the shocks.
- Map positions to factors such as equity indices, yield-curve tenors, credit spreads, FX rates and implied volatility. For a historical replay, measure those factor moves over the same dates. For a hypothetical event, specify the directions, magnitudes and economic mechanism linking the moves.
- Reprice the positions with their scenario inputs. For an initial screen, a bond's percentage price change is approximately , where the durations measure sensitivities to the benchmark yield and spread. Use full revaluation for large or nonlinear moves.
- Translate to the reporting currency, add position P&L and rank the contributors. Include cash, collateral and funding effects when the scenario calls for them. Record material factors that could only be approximated.
Under a hypothetical inflation shock, set equities down 20%, yields up 100 basis points and spreads up 150 basis points. The sleeve returns are −20%, −7% and −11%, producing an approximate 14.3% portfolio loss. The government bond sleeve contributes a gain in the first case and a loss in the second.
Use the actual exposures for the valuation date, then separate rate moves, spread moves and exchange-rate moves by factor. A single bond can respond to all three. If you use duration, show the convention for yield shocks and how you combine the rate and spread components. For options or large shocks, reprice when a linear approximation leaves out important curvature. Show contribution by sleeve and compare its sum with the full portfolio revaluation. A scenario is useful when you can explain where the loss came from.
Revalue the portfolio in Python
import numpy as np
value = 10_000_000
weights = np.array([0.50, 0.30, 0.20])
scenarios = {
"Stylised crisis": (-0.25, -100, 200),
"Inflation shock": (-0.20, 100, 150),
}
for name, (equity_return, yield_bps, spread_bps) in scenarios.items():
rate_move, spread_move = np.array([yield_bps, spread_bps]) / 10_000
sleeve_returns = np.array([
equity_return,
-7 * rate_move,
-5 * rate_move - 4 * spread_move,
])
pnl = value * weights * sleeve_returns
print(name, np.rint(pnl).astype(int), f"{pnl.sum() / value:.1%}")