Performance Attribution and Investment Alpha › Factor-Based Attribution
Quantitative FinanceAll chapters Performance Attribution and Investment Alpha › Factor-Based AttributionOne section · Sections in this chapter3 / 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
While Brinson attribution uses holdings data to decompose returns by sector, factor-based attribution uses returns data to decompose performance by exposure to systematic risk factors. This approach, rooted in the factor models we explored in Part IV Chapter 3, separates returns into two components: the portion explained by factor exposures (beta) and the residual unexplained by factors (alpha).
From CAPM to Multi-Factor Attribution
Building on our understanding of the CAPM from Part IV Chapter 2, recall that expected returns are determined by exposure to market risk. The CAPM posits a linear relationship between expected excess returns and market beta, establishing the foundational insight that investors are compensated for bearing systematic risk:
where:
- : expected return of asset
- : risk-free rate
- : sensitivity of asset to market movements
- : expected market return
This equation tells us that in equilibrium, the only source of expected excess returns should be exposure to market risk. Any return above or below this expectation represents either luck or skill. In an attribution context, we transition from this expectation to analyzing realized returns, which allows us to decompose actual performance into its constituent sources.
The realized return version of the CAPM turns the equilibrium relationship into a regression framework:
where:
- : realized return of asset
- : risk-free rate
- : alpha (excess return unexplained by beta)
- : realized market beta
- : realized market return
- : idiosyncratic error term
This regression equation sets up factor-based attribution. The term represents the return attributable to market exposure, capturing the portion of returns that any investor could have earned simply by taking on market risk. The intercept represents the average return unexplained by market exposure, the "alpha" that active managers seek to generate. If is statistically large and positive, it suggests the manager added value beyond simply taking market risk.
The residual term captures the period-by-period deviation from the linear relationship, representing idiosyncratic returns that are neither systematic nor persistent. This component averages to zero over time (by construction of OLS) and represents noise rather than skill.
Multi-factor models extend this decomposition to multiple sources of systematic risk, recognizing that the market factor alone does not capture all priced risks. Using the Fama-French three-factor model as an example, we add size and value factors to provide a richer characterization of systematic return drivers:
where:
- : realized asset return and risk-free rate
- : alpha (excess return unexplained by factor exposures)
- : exposure to market risk
- : realized market return
- : exposure to size premium (Small Minus Big)
- : exposure to value premium (High Minus Low)
- : returns of the size and value factors
- : idiosyncratic error term
Each beta coefficient measures the portfolio's sensitivity to a particular factor. A positive SMB beta indicates a tilt toward smaller companies, while a negative HML beta suggests a growth orientation. These exposures determine how much of the portfolio's return can be attributed to each systematic factor.
Factor-based attribution then decomposes total return into:
- Market contribution:
- Size contribution:
- Value contribution:
- Alpha: (residual skill)
This decomposition answers a critical question: how much of the portfolio's return came from passive exposure to known risk factors versus security selection or market timing skill? A portfolio with high factor contributions and low alpha is essentially providing factor exposure, which can be replicated cheaply through index funds or smart beta products. A portfolio with significant alpha after controlling for factors demonstrates skill that may justify higher fees.
Estimating Factor Exposures
Factor exposures are estimated using time-series regression, as covered in Part III Chapter 19. The regression framework treats factor returns as explanatory variables and portfolio excess returns as the dependent variable, allowing us to estimate the sensitivity of the portfolio to each factor.
Given periods of return data, we regress portfolio excess returns on factor returns:
where:
- : portfolio excess return at time
- : intercept (estimated alpha)
- : factor loadings
- : factor returns at time
- : residual return at time
The OLS estimates provide both the factor loadings and an estimate of alpha with associated standard errors for statistical inference. The standard errors enable hypothesis testing, allowing us to assess whether each coefficient, including alpha, is statistically distinguishable from zero.
The regression framework offers several advantages for attribution. First, it requires only return data rather than detailed holdings information, making it applicable to hedge funds and other vehicles that do not disclose positions. Second, it automatically accounts for correlations between factors through the multivariate regression framework. Third, it provides statistical inference capabilities that allow us to quantify uncertainty around our estimates.
Implementation with Simulated Data
Let's implement factor-based attribution using a realistic simulation of portfolio returns.
import numpy as np
np.random.seed(42)
# Simulate 60 months of factor returns
n_months = 60
# Factor returns (monthly)
# Market excess return: ~0.5% monthly mean, 4.5% monthly vol
mkt_rf = np.random.normal(0.005, 0.045, n_months)
# SMB (Small Minus Big): ~0.2% monthly mean, 3% monthly vol
smb = np.random.normal(0.002, 0.030, n_months)
# HML (High Minus Low): ~0.3% monthly mean, 3% monthly vol
hml = np.random.normal(0.003, 0.030, n_months)
# Risk-free rate: ~0.2% monthly
rf = np.ones(n_months) * 0.002# Generate portfolio returns with known factor exposures
# True parameters
true_alpha = 0.002 # 20 bps monthly alpha (about 2.4% annually)
true_beta_mkt = 1.10 # Slightly higher market exposure
true_beta_smb = 0.25 # Mild small-cap tilt
true_beta_hml = -0.15 # Slight growth tilt (negative value exposure)
idio_vol = 0.015 # Idiosyncratic volatility
# Generate portfolio excess returns
idio_returns = np.random.normal(0, idio_vol, n_months)
portfolio_excess = (
true_alpha
+ true_beta_mkt * mkt_rf
+ true_beta_smb * smb
+ true_beta_hml * hml
+ idio_returns
)
portfolio_returns = portfolio_excess + rf# Run factor regression
!uv pip install statsmodels
import statsmodels.api as sm
# Prepare factor matrix with constant for alpha
factors = pd.DataFrame({
'MKT_RF': mkt_rf,
'SMB': smb,
'HML': hml
})
factors_with_const = sm.add_constant(factors)
# OLS regression
model = sm.OLS(portfolio_excess, factors_with_const)
results = model.fit()
alpha_annualized = results.params['const'] * 12Factor Model Regression Results ================================================== Alpha (annualized): 3.57% Alpha t-statistic: 1.43 Alpha p-value: 0.1578 Factor Loadings: Market Beta: 1.150 SMB Beta: 0.300 HML Beta: -0.170 R-squared: 0.907
The regression recovers factor exposures close to the true values (given estimation noise). The R-squared of around 70-80% indicates that the three factors explain most of the portfolio's return variation, with the remainder being idiosyncratic. The alpha estimate and its t-statistic allow us to assess whether the manager added value beyond factor exposures.


Return Attribution by Factor
With the factor loadings estimated, we can decompose realized returns into contributions from each factor.
# Calculate return contributions
alpha_est = results.params["const"]
beta_mkt = results.params["MKT_RF"]
beta_smb = results.params["SMB"]
beta_hml = results.params["HML"]
# Average monthly factor returns
avg_mkt = np.mean(mkt_rf)
avg_smb = np.mean(smb)
avg_hml = np.mean(hml)
avg_port_excess = np.mean(portfolio_excess)
# Return contributions (monthly)
mkt_contribution = beta_mkt * avg_mkt
smb_contribution = beta_smb * avg_smb
hml_contribution = beta_hml * avg_hml
total_monthly_contribution = (
alpha_est + mkt_contribution + smb_contribution + hml_contribution
)Return Attribution (Monthly) ---------------------------------------- Average Portfolio Excess Return: 0.031% Decomposition: Alpha: 0.297% Market Factor: -0.225% Size Factor (SMB): 0.057% Value Factor (HML): -0.098% Total: 0.031%
# Annualized attribution parameter
ann_factor = 12
# Calculate annualized metrics
avg_port_excess_ann = avg_port_excess * ann_factor
alpha_ann = alpha_est * ann_factor
mkt_contrib_ann = mkt_contribution * ann_factor
smb_contrib_ann = smb_contribution * ann_factor
hml_contrib_ann = hml_contribution * ann_factorReturn Attribution (Annualized) ---------------------------------------- Average Portfolio Excess Return: 0.37% Decomposition: Alpha: 3.57% Market Factor: -2.70% Size Factor (SMB): 0.68% Value Factor (HML): -1.18%
In this seeded 60-month sample, alpha is the largest positive component at 3.57% annualized. The realized Market and Value factor means are negative, so the portfolio's MKT and HML exposures detract 2.70% and 1.18%, respectively, while SMB adds 0.68%. Together, the four components reconcile to the sample's 0.37% annualized average excess return. This result is specific to the simulated sample rather than a claim about long-run factor premia.
Visualization of Factor Contributions

Risk Attribution
Beyond return attribution, factor models also allow us to attribute portfolio risk (variance) to factor sources. This analysis answers a complementary question: where does portfolio volatility come from?
The mathematical foundation for risk attribution relies on a key property of OLS regression. Because the regression residuals are uncorrelated with the factors by construction of ordinary least squares, the variance of the sum equals the sum of the variances. This orthogonality allows us to cleanly separate systematic from idiosyncratic risk.
Thus, the total variance decomposes into systematic and idiosyncratic components:
where:
- : total variance of portfolio excess returns
- : vector of factor loadings
- : factor covariance matrix
- : idiosyncratic variance
The first term, , captures systematic variance arising from factor exposures. This quadratic form accounts for each factor's individual variance and for the correlations between factors. When factors are positively correlated, systematic risk may be amplified; when negatively correlated, they may partially offset. The second term, , represents idiosyncratic variance that cannot be attributed to any factor, which reflects security-specific risks that diversify away in large portfolios.
# Factor covariance matrix
factor_cov = factors.cov()
# Factor betas as vector
betas = np.array([beta_mkt, beta_smb, beta_hml])
# Systematic variance
systematic_var = betas @ factor_cov.values @ betas
# Idiosyncratic variance (from regression residuals)
idio_var = np.var(results.resid, ddof=4) # 4 parameters estimated
# Reconcile the two modeled components to a common attributed total.
# The separately estimated sample variance differs slightly because it uses a
# different degrees-of-freedom convention.
observed_var = np.var(portfolio_excess, ddof=1)
total_var = systematic_var + idio_var
# Risk ratios
syst_ratio = systematic_var / total_var
idio_ratio = idio_var / total_var
# Annualized volatilities
total_vol_ann = np.sqrt(total_var * 12)
syst_vol_ann = np.sqrt(systematic_var * 12)
idio_vol_ann = np.sqrt(idio_var * 12)Risk Attribution (Monthly Variance) ---------------------------------------- Observed Variance: 0.002528 Attributed Variance: 0.002540 Systematic Variance: 0.002292 (90.2%) Idiosyncratic Variance:0.000248 (9.8%) Annualized Volatility: Total: 17.46% Systematic: 16.59% Idiosyncratic: 5.46%
