Skip to content

Fixed Income Active Attribution

1. Calculation Name

Fixed Income Active Attribution (Carry/Curve/Spread/FX/Selection)

2. Description and Mathematical Formula

The active attribution plugin decomposes the excess return of a fixed-income portfolio into component effects using key rate durations, spread sensitivities, and FX exposures.

For each factor \( f \):

  • Portfolio contribution \( C^P_f \)
  • Benchmark contribution \( C^B_f \)
  • Active effect \( C^{A}_f = C^P_f - C^B_f \)

Underlying calculations derive \( C^P_f \) and \( C^B_f \) by multiplying factor exposures (DV01, spread DV01, FX notional) by observed factor moves.

3. Input Sample Data

Component Portfolio Contribution (bps) Benchmark Contribution (bps)
Carry 35.2 28.7
Curve 12.4 10.1
Spread 58.0 44.5
FX 4.2 6.0
Selection 18.9 12.5

4. Mathematical Solution

  • Active effects:
    Carry \(= 35.2 - 28.7 = 6.5\) bps
    Curve \(= 12.4 - 10.1 = 2.3\) bps
    Spread \(= 58.0 - 44.5 = 13.5\) bps
    FX \(= 4.2 - 6.0 = -1.8\) bps
    Selection \(= 18.9 - 12.5 = 6.4\) bps
  • Total active return \( = 6.5 + 2.3 + 13.5 - 1.8 + 6.4 = 26.9 \) bps
  • Expressed as percentage: \( 0.269\% \) excess performance for the horizon.

5. Sample Python and R Code

import pandas as pd

data = pd.DataFrame(
    {
        "component": ["Carry", "Curve", "Spread", "FX", "Selection"],
        "portfolio_bps": [35.2, 12.4, 58.0, 4.2, 18.9],
        "benchmark_bps": [28.7, 10.1, 44.5, 6.0, 12.5],
    }
)

data["active_bps"] = data["portfolio_bps"] - data["benchmark_bps"]
data["active_pct"] = data["active_bps"] / 10000
total_active = data["active_bps"].sum() / 10000
print(data)
print(f"Total excess return: {total_active:.4%}")
library(dplyr)

data <- tibble::tibble(
  component = c("Carry", "Curve", "Spread", "FX", "Selection"),
  portfolio_bps = c(35.2, 12.4, 58.0, 4.2, 18.9),
  benchmark_bps = c(28.7, 10.1, 44.5, 6.0, 12.5)
)

data <- data %>%
  mutate(
    active_bps = portfolio_bps - benchmark_bps,
    active_pct = active_bps / 10000
  )

total_active <- sum(data$active_bps) / 10000
data
scales::percent(total_active, accuracy = 0.01)

6. Output Table

Component Active Contribution (bps)
Carry +6.5
Curve +2.3
Spread +13.5
FX -1.8
Selection +6.4
Total +26.9

7. Conclusion

FinFacts’ active attribution distills how rates, spreads, currencies, and security selection drive excess performance. Use this template when documenting investment commentary or validating the plugin against spreadsheet prototypes.