Brinson–Fachler Attribution (A/S/I)¶
1. Calculation Name¶
Brinson–Fachler Allocation, Selection, Interaction Attribution
2. Description and Mathematical Formula¶
Using Brinson–Fachler arithmetic attribution, performance is decomposed into:
- Allocation effect: \( A_i = (w^P_i - w^B_i) \times (r^B_i - R^B) \)
- Selection effect: \( S_i = w^B_i \times (r^P_i - r^B_i) \)
- Interaction effect: \( I_i = (w^P_i - w^B_i) \times (r^P_i - r^B_i) \)
Where:
- \( w^P_i, r^P_i \) are portfolio weights and returns
- \( w^B_i, r^B_i \) are benchmark weights and returns
- \( R^B = \sum_i w^B_i r^B_i \) is the total benchmark return
- The active return \( R^P - R^B = \sum_i (A_i + S_i + I_i) \)
3. Input Sample Data¶
| Segment | \( w^P_i \) | \( r^P_i \) | \( w^B_i \) | \( r^B_i \) |
|---|---|---|---|---|
| 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% |
Benchmark return \( R^B = 2.56\% \).
4. Mathematical Solution¶
- Allocation effects (bps):
Government \(= (0.35 - 0.40) \times (1.80\% - 2.56\%) = +3.8\)
Credit \(= (0.30 - 0.25) \times (3.80\% - 2.56\%) = +6.2\)
Mortgages \(= (0.15 - 0.20) \times (3.00\% - 2.56\%) = -2.2\)
High Yield \(= (0.10 - 0.05) \times (5.00\% - 2.56\%) = +12.2\)
Cash \(≈ 0.0\) - Selection effects (bps):
Government \(= 0.40 \times (2.10\% - 1.80\%) = +12.0\)
Credit \(= 0.25 \times (4.50\% - 3.80\%) = +17.5\)
Mortgages \(= 0.20 \times (3.20\% - 3.00\%) = +4.0\)
High Yield \(= 0.05 \times (6.50\% - 5.00\%) = +7.5\)
Cash \(= 0.10 \times (0.50\% - 0.40\%) = +1.0\) - Interaction effects (bps):
Government \(= (0.35 - 0.40) \times (2.10\% - 1.80\%) = -1.5\)
Credit \(= (0.30 - 0.25) \times (4.50\% - 3.80\%) = +3.5\)
Mortgages \(= (0.15 - 0.20) \times (3.20\% - 3.00\%) = -1.0\)
High Yield \(= (0.10 - 0.05) \times (6.50\% - 5.00\%) = +7.5\)
Cash \(≈ 0.0\) - Totals:
Allocation = 20.0 bps
Selection = 42.0 bps
Interaction = 8.5 bps
Active return \(= 0.705\%\) (70.5 bps)
5. Sample Python and R Code¶
import pandas as pd
data = pd.DataFrame(
{
"segment": ["Government", "Credit", "Mortgages", "High Yield", "Cash"],
"w_p": [0.35, 0.30, 0.15, 0.10, 0.10],
"r_p": [0.021, 0.045, 0.032, 0.065, 0.005],
"w_b": [0.40, 0.25, 0.20, 0.05, 0.10],
"r_b": [0.018, 0.038, 0.030, 0.050, 0.004],
}
)
R_b = (data["w_b"] * data["r_b"]).sum()
data["allocation"] = (data["w_p"] - data["w_b"]) * (data["r_b"] - R_b)
data["selection"] = data["w_b"] * (data["r_p"] - data["r_b"])
data["interaction"] = (data["w_p"] - data["w_b"]) * (data["r_p"] - data["r_b"])
totals = data[["allocation", "selection", "interaction"]].sum()
active = totals.sum()
print(data)
print(totals, active)
library(dplyr)
data <- tibble::tibble(
segment = c("Government", "Credit", "Mortgages", "High Yield", "Cash"),
w_p = c(0.35, 0.30, 0.15, 0.10, 0.10),
r_p = c(0.021, 0.045, 0.032, 0.065, 0.005),
w_b = c(0.40, 0.25, 0.20, 0.05, 0.10),
r_b = c(0.018, 0.038, 0.030, 0.050, 0.004)
)
R_b <- sum(data$w_b * data$r_b)
data <- data %>%
mutate(
allocation = (w_p - w_b) * (r_b - R_b),
selection = w_b * (r_p - r_b),
interaction = (w_p - w_b) * (r_p - r_b)
)
totals <- summarise(data,
allocation = sum(allocation),
selection = sum(selection),
interaction = sum(interaction)
)
active <- sum(totals)
data
totals
active
6. Output Table¶
| Segment | Allocation (bps) | Selection (bps) | Interaction (bps) |
|---|---|---|---|
| Government | +3.8 | +12.0 | -1.5 |
| Credit | +6.2 | +17.5 | +3.5 |
| Mortgages | -2.2 | +4.0 | -1.0 |
| High Yield | +12.2 | +7.5 | +7.5 |
| Cash | +0.0 | +1.0 | +0.0 |
| Total | +20.0 | +42.0 | +8.5 |
7. Conclusion¶
Brinson–Fachler attribution highlights whether excess performance comes from allocation tilts, security selection, or their interaction. This template aligns with FinFacts’ reporting views and is a reliable reference for QA, demos, and documentation.