Skip to content

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.