Performance Attribution and Investment Alpha › Combined Attribution Example
Quantitative FinanceAll chapters Performance Attribution and Investment Alpha › Combined Attribution ExampleOne section · Sections in this chapter6 / 10 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's bring together Brinson and factor-based attribution in a combined analysis that demonstrates how both frameworks complement each other.
import numpy as np
# Comprehensive attribution for a hypothetical equity portfolio
# Using realistic sector and factor data
np.random.seed(123)
# Define sectors and their characteristics
sector_names = [
"Technology",
"Healthcare",
"Financials",
"Consumer Disc.",
"Industrials",
]
n_sectors = len(sector_names)
# Portfolio construction
port_weights = np.array([0.35, 0.20, 0.15, 0.18, 0.12])
bench_weights = np.array(
[0.28, 0.14, 0.13, 0.10, 0.11]
) # Sum less than 1 (other sectors)
bench_weights = bench_weights / bench_weights.sum() # Normalize
# Sector returns (one period)
bench_sector_returns = np.array([0.082, 0.045, 0.058, 0.035, 0.042])
port_sector_returns = np.array([0.095, 0.052, 0.048, 0.038, 0.055])# Brinson Attribution
total_port_ret = np.sum(port_weights * port_sector_returns)
total_bench_ret = np.sum(bench_weights * bench_sector_returns)
active_ret = total_port_ret - total_bench_ret
weight_diff = port_weights - bench_weights
return_diff = port_sector_returns - bench_sector_returns
sector_excess_ret = bench_sector_returns - total_bench_ret
alloc = weight_diff * sector_excess_ret
selec = bench_weights * return_diff
inter = weight_diff * return_diff
total_active = np.sum(alloc) + np.sum(selec) + np.sum(inter)============================================================ COMPREHENSIVE PERFORMANCE ATTRIBUTION ============================================================ 1. HOLDINGS-BASED (BRINSON) ATTRIBUTION -------------------------------------------------- Total Portfolio Return: 6.43% Total Benchmark Return: 5.91% Active Return: 0.52% Allocation Effect: -0.14% Selection Effect: +0.66% Interaction Effect: -0.01% Total: +0.52%
# Sector-level Brinson breakdown data
brinson_df = pd.DataFrame(
{
"Sector": sector_names,
"Port Wgt": port_weights,
"Bench Wgt": bench_weights,
"Allocation": alloc,
"Selection": selec,
"Interaction": inter,
"Total Contrib": alloc + selec + inter,
}
)Sector-Level Breakdown: -------------------------------------------------- Technology: Weights: Port 35.0% vs Bench 36.8% Allocation: -0.04%, Selection: +0.48% Healthcare: Weights: Port 20.0% vs Bench 18.4% Allocation: -0.02%, Selection: +0.13% Financials: Weights: Port 15.0% vs Bench 17.1% Allocation: +0.00%, Selection: -0.17% Consumer Disc.: Weights: Port 18.0% vs Bench 13.2% Allocation: -0.12%, Selection: +0.04% Industrials: Weights: Port 12.0% vs Bench 14.5% Allocation: +0.04%, Selection: +0.19%

Interpreting Attribution Results
The Brinson analysis shows that the portfolio's {python} f"{total_active:.2%}" outperformance is driven by selection, which contributes {python} f"{np.sum(selec):+.2%}" overall. Technology provides the largest selection contribution ({python} f"{selec[0]:+.2%}"), followed by Industrials ({python} f"{selec[4]:+.2%}"), while Financials detracts ({python} f"{selec[2]:+.2%}"). Allocation subtracts {python} f"{np.sum(alloc):+.2%}" in aggregate, including a small negative Technology allocation effect of {python} f"{alloc[0]:+.2%}".
From a factor perspective, a manager might further analyze whether the Technology outperformance came from factor tilts within tech (e.g., overweighting growth-oriented tech stocks) or from stock selection orthogonal to known factors. This level of analysis requires combining holdings data with factor exposure data, a technique used by sophisticated institutional investors.