Fixed Income Scenario Analysis¶
1. Calculation Name¶
Scenario Sensitivity (Rate and Spread Shocks)
2. Description and Mathematical Formula¶
Scenario analysis shocks the portfolio and benchmark using predefined rate/spread moves. For scenario \( s \):
\[ \mathrm{PnL}^P_s = \sum_{k} \mathrm{Exposure}^P_{k} \times \Delta_s z_{k}, \quad \mathrm{Active}_s = \mathrm{PnL}^P_s - \mathrm{PnL}^B_s \]
where \( \Delta_s z_{k} \) is the shock applied to factor \( k \) (e.g., key rate shift, spread change, FX move).
3. Input Sample Data¶
| Scenario | Portfolio PnL (bps) | Benchmark PnL (bps) | Active PnL (bps) | Description |
|---|---|---|---|---|
| Parallel +50 bp | -420 | -480 | +60 | Uniform +50 bp shift |
| Parallel -50 bp | +410 | +460 | -50 | Uniform -50 bp shift |
| Steepener (+25 / -25) | -110 | -140 | +30 | 30Y +25 bp, 2Y -25 bp |
| Flattener (-25 / +25) | +105 | +80 | +25 | 30Y -25 bp, 2Y +25 bp |
| Credit +75 bp | -250 | -290 | +40 | Corporate spreads +75 bp |
4. Mathematical Solution¶
- Active PnL for each scenario is simply the difference between portfolio and benchmark PnL.
- Example (Parallel +50 bp): \( -420 - (-480) = +60 \) bps.
- Aggregate check: the plugin also reports the worst loss (here -420 bps) and best gain (+410 bps) for quick risk triage.
5. Sample Python and R Code¶
import pandas as pd
scenarios = pd.DataFrame(
{
"scenario": [
"Parallel +50 bp",
"Parallel -50 bp",
"Steepener (+25 / -25)",
"Flattener (-25 / +25)",
"Credit +75 bp",
],
"pnl_port": [-420, 410, -110, 105, -250],
"pnl_bench": [-480, 460, -140, 80, -290],
}
)
scenarios["active_bps"] = scenarios["pnl_port"] - scenarios["pnl_bench"]
print(scenarios)
print("Worst-case:", scenarios["pnl_port"].min(), "bps")
print("Best-case:", scenarios["pnl_port"].max(), "bps")
library(dplyr)
scenarios <- tibble::tibble(
scenario = c(
"Parallel +50 bp",
"Parallel -50 bp",
"Steepener (+25 / -25)",
"Flattener (-25 / +25)",
"Credit +75 bp"
),
pnl_port = c(-420, 410, -110, 105, -250),
pnl_bench = c(-480, 460, -140, 80, -290)
)
scenarios <- scenarios %>%
mutate(active_bps = pnl_port - pnl_bench)
worst_case <- min(scenarios$pnl_port)
best_case <- max(scenarios$pnl_port)
scenarios
worst_case
best_case
6. Output Table¶
| Scenario | Active PnL (bps) | Risk Note |
|---|---|---|
| Parallel +50 bp | +60 | Portfolio less rate-sensitive than benchmark |
| Parallel -50 bp | -50 | Underperforms rally due to shorter duration |
| Steepener (+25 / -25) | +30 | Beneficial overweight in long maturities |
| Flattener (-25 / +25) | +25 | Gains from barbell positioning |
| Credit +75 bp | +40 | Portfolio has tighter credit risk than benchmark |
7. Conclusion¶
Scenario analytics give a forward-looking view of rate and spread risks. This template captures the essence of the FinFacts scenario plugin, helping teams document stress results alongside attribution and performance metrics.