Skip to content

Contribution to Return

1. Calculation Name

Contribution to Return (CTR)

2. Description and Mathematical Formula

CTR attributes portfolio performance by multiplying each segment’s weight by its return. The excess contribution compares portfolio and benchmark contributions.

For segment \( i \):

\[ \text{CTR}^P_i = w^P_i \times r^P_i, \quad \text{CTR}^B_i = w^B_i \times r^B_i, \quad \text{Active}_i = \text{CTR}^P_i - \text{CTR}^B_i. \]

3. Input Sample Data

Segment Portfolio Weight Portfolio Return Benchmark Weight Benchmark Return
Government 35% 2.10% 40% 1.80%
Credit 30% 4.50% 25% 3.80%
Mortgages 15% 3.20% 20% 3.00%
High Yield 10% 6.50% 5% 5.00%
Cash 10% 0.50% 10% 0.40%

4. Mathematical Solution

  • Portfolio contribution (bps):
    Government \(= 0.35 \times 2.10\% = 0.735\%\)
    Credit \(= 0.30 \times 4.50\% = 1.350\%\)
    Mortgages \(= 0.15 \times 3.20\% = 0.480\%\)
    High Yield \(= 0.10 \times 6.50\% = 0.650\%\)
    Cash \(= 0.10 \times 0.50\% = 0.050\%\)
  • Benchmark contribution:
    Government \(= 0.40 \times 1.80\% = 0.720\%\)
    Credit \(= 0.25 \times 3.80\% = 0.950\%\)
    Mortgages \(= 0.20 \times 3.00\% = 0.600\%\)
    High Yield \(= 0.05 \times 5.00\% = 0.250\%\)
    Cash \(= 0.10 \times 0.40\% = 0.040\%\)
  • Active contribution (bps):
    Government \(= 0.015\%\)
    Credit \(= 0.400\%\)
    Mortgages \(= -0.120\%\)
    High Yield \(= 0.400\%\)
    Cash \(= 0.010\%\)
  • Totals:
    Portfolio contribution \(= 3.265\%\)
    Benchmark contribution \(= 2.560\%\)
    Excess contribution \(= 0.705\%\)

5. Sample Python and R Code

import pandas as pd

data = pd.DataFrame(
    {
        "segment": ["Government", "Credit", "Mortgages", "High Yield", "Cash"],
        "w_port": [0.35, 0.30, 0.15, 0.10, 0.10],
        "r_port": [0.021, 0.045, 0.032, 0.065, 0.005],
        "w_bench": [0.40, 0.25, 0.20, 0.05, 0.10],
        "r_bench": [0.018, 0.038, 0.030, 0.050, 0.004],
    }
)

data["ctr_port"] = data["w_port"] * data["r_port"]
data["ctr_bench"] = data["w_bench"] * data["r_bench"]
data["ctr_active"] = data["ctr_port"] - data["ctr_bench"]

totals = data[["ctr_port", "ctr_bench", "ctr_active"]].sum()
print(data)
print(totals)
library(dplyr)

data <- tibble::tibble(
  segment = c("Government", "Credit", "Mortgages", "High Yield", "Cash"),
  w_port = c(0.35, 0.30, 0.15, 0.10, 0.10),
  r_port = c(0.021, 0.045, 0.032, 0.065, 0.005),
  w_bench = c(0.40, 0.25, 0.20, 0.05, 0.10),
  r_bench = c(0.018, 0.038, 0.030, 0.050, 0.004)
)

data <- data %>%
  mutate(
    ctr_port = w_port * r_port,
    ctr_bench = w_bench * r_bench,
    ctr_active = ctr_port - ctr_bench
  )

totals <- summarise(data,
  ctr_port = sum(ctr_port),
  ctr_bench = sum(ctr_bench),
  ctr_active = sum(ctr_active)
)
data
totals

6. Output Table

Segment Portfolio CTR Benchmark CTR Active (bps)
Government 0.735% 0.720% +1.5
Credit 1.350% 0.950% +40.0
Mortgages 0.480% 0.600% -12.0
High Yield 0.650% 0.250% +40.0
Cash 0.050% 0.040% +1.0
Total 3.265% 2.560% +70.5

7. Conclusion

CTR quantifies how much each sleeve contributes to portfolio and benchmark performance. FinFacts uses this foundation for downstream attribution charts and sanity checks, making this template useful for QA notebooks and training decks.