Statistical / ML for QuantsFactor ModelsCAPMFama-FrenchRisk Premia

Factor Models: CAPM, APT, and Fama-French

22 min readLevel: Medium

Setup

Context and Assumptions

Factor models decompose asset returns into systematic exposures to common risk factors and an idiosyncratic residual. They are used on every quant equity desk — for risk attribution, portfolio construction, and alpha signal design.

The central question factor models answer is: why do different assets earn different expected returns? The answer, in all factor frameworks, is compensation for bearing systematic risk that cannot be diversified away.

Notation throughout. Let:

  • Ri,tR_{i,t} = excess return of asset ii at time tt (return minus risk-free rate rfr_f)
  • E[Ri]\mathbb{E}[R_i] = unconditional expected excess return of asset ii
  • βi,k\beta_{i,k} = factor loading (sensitivity) of asset ii to factor kk
  • λk\lambda_k = risk premium for factor kk (expected excess return per unit of factor exposure)
  • εi,t\varepsilon_{i,t} = idiosyncratic return; E[εi,t]=0\mathbb{E}[\varepsilon_{i,t}] = 0, uncorrelated with factors

Key assumptions that vary by model are stated in each section.


Theory

1. CAPM: Capital Asset Pricing Model

Assumptions.

  1. Investors are mean-variance optimisers (Markowitz 1952) with identical beliefs.
  2. All assets are tradeable; no transaction costs, taxes, or short-selling constraints.
  3. Returns are jointly normally distributed (or investors have quadratic utility).
  4. A risk-free asset exists, lendable and borrowable at rate rfr_f.
  5. All investors have the same investment horizon.

Under these assumptions, every investor holds the same risky portfolio — the market portfolio MM, which in equilibrium is the value-weighted portfolio of all risky assets.

Derivation of the SML. Consider any asset ii. Form a portfolio with weight α\alpha in asset ii and (1α)(1-\alpha) in the market portfolio. Expected excess return and variance:

E[Rp]=αE[Ri]+(1α)E[RM]\mathbb{E}[R_p] = \alpha \mathbb{E}[R_i] + (1-\alpha)\mathbb{E}[R_M] σp2=α2σi2+2α(1α)σiM+(1α)2σM2\sigma_p^2 = \alpha^2 \sigma_i^2 + 2\alpha(1-\alpha)\sigma_{iM} + (1-\alpha)^2 \sigma_M^2

In equilibrium, asset ii is already in the market portfolio, so the efficient portfolio locus through (i,M)(i, M) must be tangent to the Capital Market Line at α=0\alpha = 0. Computing dE[Rp]/dσpd\mathbb{E}[R_p]/d\sigma_p at α=0\alpha = 0 and equating to the Sharpe ratio of the market gives:

E[Ri]=βiE[RM],βi=Cov(Ri,RM)Var(RM)=σiMσM2.\mathbb{E}[R_i] = \beta_i \, \mathbb{E}[R_M], \qquad \beta_i = \frac{\text{Cov}(R_i, R_M)}{\text{Var}(R_M)} = \frac{\sigma_{iM}}{\sigma_M^2}.

This is the Security Market Line (SML). Assets plot on the SML in equilibrium; deviations from it are alphas — excess returns not explained by market beta.

Time-series regression form (Jensen 1968):

Ri,t=αi+βiRM,t+εi,t.R_{i,t} = \alpha_i + \beta_i R_{M,t} + \varepsilon_{i,t}.

Under CAPM, αi=0\alpha_i = 0 for all assets in equilibrium.

2. APT: Arbitrage Pricing Theory

Assumptions (Ross 1976).

  1. Returns are generated by a K-factor linear model: Ri,t=E[Ri]+k=1Kβi,kFk,t+εi,t,R_{i,t} = \mathbb{E}[R_i] + \sum_{k=1}^K \beta_{i,k} F_{k,t} + \varepsilon_{i,t}, where Fk,tF_{k,t} are zero-mean factor realisations and εi,t\varepsilon_{i,t} are idiosyncratic, mutually uncorrelated, with bounded variance.
  2. There are sufficiently many assets to form well-diversified portfolios.
  3. No-arbitrage: no portfolio with zero cost, zero systematic risk, and positive expected return.

Result. In a no-arbitrage economy, expected returns satisfy (approximately):

E[Ri]=λ0+k=1Kβi,kλk,\mathbb{E}[R_i] = \lambda_0 + \sum_{k=1}^K \beta_{i,k} \lambda_k,

where λ0=rf\lambda_0 = r_f (zero-beta return) and λk\lambda_k is the risk premium for factor kk. The APT does not specify which factors matter — it only says that if factors explain covariance structure, their premia must exist to preclude arbitrage.

CAPM is a special case of APT with K=1K=1 and F1=RMF_1 = R_M, plus the equilibrium assumptions that fix λ1=E[RM]\lambda_1 = \mathbb{E}[R_M].

3. Fama-French Three-Factor Model

Motivation. Fama and French (1992, 1993) documented that CAPM beta does not fully explain the cross-section of expected returns. Two anomalies survive controlling for market beta:

  • Size effect: small-cap stocks earn higher average returns than large-cap.
  • Value effect: stocks with high book-to-market (B/M) ratio earn higher average returns than growth stocks.

Factor construction (Fama-French 1993).

Let SMBtSMB_t (Small Minus Big) and HMLtHML_t (High Minus Low) be zero-cost long-short factor portfolios constructed monthly:

  • SMB: long bottom 50% of stocks by market cap, short top 50%.
  • HML: long top 30% of stocks by B/M, short bottom 30%; sort within size buckets to control for size.

The three-factor model:

Ri,t=αi+βiMRM,t+βiSMBSMBt+βiHMLHMLt+εi,t.R_{i,t} = \alpha_i + \beta_i^M R_{M,t} + \beta_i^{SMB} \, SMB_t + \beta_i^{HML} \, HML_t + \varepsilon_{i,t}.

Under the model, αi=0\alpha_i = 0 in equilibrium. Empirically, αi\alpha_i is close to zero for most equity portfolios but non-zero for momentum strategies — motivating Carhart (1997) to add a momentum factor MOMMOM.

Fama-MacBeth two-pass regression. The canonical approach to estimating factor premia in the cross-section:

  1. First pass (time-series): For each asset ii, regress Ri,tR_{i,t} on factors to estimate β^i,k\hat{\beta}_{i,k}.
  2. Second pass (cross-section): At each time tt, regress Ri,tR_{i,t} on estimated betas: Ri,t=γ0,t+kγk,tβ^i,k+ui,t.R_{i,t} = \gamma_{0,t} + \sum_k \gamma_{k,t} \hat{\beta}_{i,k} + u_{i,t}. Average the cross-sectional estimates: λ^k=T1tγ^k,t\hat{\lambda}_k = T^{-1} \sum_t \hat{\gamma}_{k,t}. Standard errors use the time-series standard deviation of γ^k,t\hat{\gamma}_{k,t}, which is robust to cross-sectional heteroskedasticity (but not to estimation error in betas — the Shanken correction applies).

Implementation

"""
Factor model estimation: CAPM and Fama-French three-factor.

Assumptions:
- Returns are in excess of the risk-free rate (monthly frequency)
- Factor data follows Ken French's construction conventions
- All returns and factors are in decimal form (not percent)
"""

from __future__ import annotations

import numpy as np
import pandas as pd
from scipy import stats
import statsmodels.api as sm
from typing import NamedTuple


class FactorRegressionResult(NamedTuple):
    alpha: float           # annualised intercept
    alpha_tstat: float     # t-statistic on alpha
    betas: dict[str, float]
    r_squared: float
    residual_std: float    # annualised idiosyncratic vol


def capm_regression(
    excess_returns: pd.Series,
    market_excess_return: pd.Series,
) -> FactorRegressionResult:
    """
    Estimate CAPM beta via OLS. Both series must be monthly excess returns.
    Alpha is annualised (multiplied by 12).
    """
    X = sm.add_constant(market_excess_return.rename("MKT"))
    model = sm.OLS(excess_returns, X).fit()

    alpha_monthly = model.params["const"]
    alpha_tstat = model.tvalues["const"]

    return FactorRegressionResult(
        alpha=alpha_monthly * 12,
        alpha_tstat=alpha_tstat,
        betas={"MKT": model.params["MKT"]},
        r_squared=model.rsquared,
        residual_std=model.resid.std() * np.sqrt(12),
    )


def fama_french_regression(
    excess_returns: pd.Series,
    factors: pd.DataFrame,   # columns: MKT, SMB, HML (monthly excess returns)
) -> FactorRegressionResult:
    """
    Estimate Fama-French three-factor model via OLS.
    factors must contain columns ['MKT', 'SMB', 'HML'].
    """
    required = {"MKT", "SMB", "HML"}
    if not required.issubset(factors.columns):
        raise ValueError(f"factors must contain columns {required}")

    X = sm.add_constant(factors[["MKT", "SMB", "HML"]])
    model = sm.OLS(excess_returns, X).fit()

    alpha_monthly = model.params["const"]
    alpha_tstat = model.tvalues["const"]

    return FactorRegressionResult(
        alpha=alpha_monthly * 12,
        alpha_tstat=alpha_tstat,
        betas={k: model.params[k] for k in ["MKT", "SMB", "HML"]},
        r_squared=model.rsquared,
        residual_std=model.resid.std() * np.sqrt(12),
    )


def fama_macbeth(
    returns: pd.DataFrame,    # T x N matrix of monthly excess returns
    factors: pd.DataFrame,    # T x K matrix of factor returns
    shanken_correction: bool = True,
) -> pd.DataFrame:
    """
    Fama-MacBeth two-pass cross-sectional regression.

    Pass 1: time-series OLS per asset → beta estimates.
    Pass 2: cross-sectional OLS at each t → gamma_t estimates.

    Returns DataFrame with columns: lambda (mean gamma), t_stat, std_error.
    Shanken (1992) correction adjusts SE for errors-in-variables from beta estimation.
    """
    T, N = returns.shape
    K = factors.shape[1]
    factor_names = list(factors.columns)

    # --- Pass 1: estimate betas ---
    X_ts = sm.add_constant(factors)
    betas = np.zeros((N, K))     # N assets x K factors
    for i, asset in enumerate(returns.columns):
        res = sm.OLS(returns[asset], X_ts).fit()
        betas[i] = [res.params[k] for k in factor_names]

    # --- Pass 2: cross-sectional regression at each t ---
    gammas = np.zeros((T, K + 1))   # intercept + K factor premia
    for t in range(T):
        y_t = returns.iloc[t].values
        X_cs = sm.add_constant(betas)
        res_t = sm.OLS(y_t, X_cs).fit()
        gammas[t] = res_t.params

    lambda_hat = gammas.mean(axis=0)
    se_raw = gammas.std(axis=0, ddof=1) / np.sqrt(T)

    if shanken_correction:
        # Shanken (1992): inflate SE by (1 + lambda' Sigma_F^{-1} lambda)
        sigma_f = np.cov(factors.values.T, ddof=1)
        lam_k = lambda_hat[1:]   # factor premia only
        c = 1.0 + lam_k @ np.linalg.inv(sigma_f) @ lam_k
        se_raw[1:] *= np.sqrt(c)

    t_stats = lambda_hat / se_raw

    index = ["intercept"] + factor_names
    return pd.DataFrame({
        "lambda": lambda_hat,
        "t_stat": t_stats,
        "std_error": se_raw,
    }, index=index)

Validation

CAPM. A correct implementation satisfies:

  • For the market portfolio itself: β=1\beta = 1, α=0\alpha = 0, R2=1R^2 = 1 (by construction).
  • For the risk-free asset: β=0\beta = 0, α=0\alpha = 0, R2=0R^2 = 0.

Fama-French. Using monthly data from Ken French's data library (publicly available at mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html):

  • SMB average monthly return ≈ +0.20% (1963–2022); HML ≈ +0.37%.
  • Small-cap value portfolios load positively on both SMB and HML; large-cap growth loads negatively.
  • The three-factor model explains R2>0.95R^2 > 0.95 for most size/B-M sorted decile portfolios, versus R20.75R^2 \approx 0.75 for CAPM alone.

Fama-MacBeth. Applying to 25 Fama-French size/B-M portfolios over 1963–2022 should yield:

  • Market premium λ^M0.5%\hat{\lambda}_M \approx 0.5\% per month (t-stat ≈ 2.0).
  • SMB premium λ^SMB0.2%\hat{\lambda}_{SMB} \approx 0.2\% (t-stat ≈ 1.8).
  • HML premium λ^HML0.3%\hat{\lambda}_{HML} \approx 0.3\% (t-stat ≈ 2.3).

Limitations

CAPM: Empirically Rejected

CAPM assumes homogeneous beliefs, no frictions, and a mean-variance world. In practice:

  • The SML is too flat: low-beta stocks earn higher risk-adjusted returns than CAPM predicts (Black, Jensen, Scholes 1972); high-beta stocks underperform. This underlies the low-volatility anomaly.
  • The true market portfolio is unobservable (Roll's critique, 1977): the value-weighted equity index is not the market portfolio (it excludes human capital, real estate, private equity, foreign assets). CAPM is therefore not testable in principle.

Fama-French: The Factor Zoo

Harvey, Liu, and Zhu (2016) documented over 300 claimed cross-sectional return predictors. Most are likely false discoveries:

  • Data snooping: factors found in-sample lose significance out-of-sample.
  • Non-stationarity: the HML value premium has been weak since 2007; the size premium has been disputed.
  • Structural explanation: Fama and French view the factors as risk premia; others (behavioural finance) attribute them to mispricing. The distinction matters for whether premia persist.

Estimation Errors in Betas

OLS beta estimates are noisy, especially from short time-series. The Shanken (1992) errors-in-variables correction partially addresses this, but shrinkage estimators (James-Stein, Ledoit-Wolf) or Bayesian shrinkage (Vasicek 1973) often outperform in practice for portfolio construction.

Changing Factor Premia

Factor premia are not stationary. Post-publication decay (McLean and Pontiff 2016) is well-documented: anomalies shrink by roughly 25% after academic publication as capital arbitrages them away. Factor models trained on historical data should be validated on out-of-sample periods; the period should not overlap the discovery sample.


Interview Angle

L1. Derive the CAPM Security Market Line. What is beta, and how do you estimate it? Why does drift μ\mu not appear in Black-Scholes but expected return does appear in the CAPM? (Answer: BS is a no-arbitrage argument under risk neutrality; CAPM is an equilibrium theory about required returns under physical measure.)

L2. Explain Fama-MacBeth and why it's preferred over a single pooled panel regression for estimating factor premia. What is the Shanken correction and why is it needed? Compare the economic interpretation of SMB and HML under the rational risk story versus the behavioural story.

Fama-MacBeth vs pooled OLS. A pooled OLS of RitR_{it} on betas treats all T×NT \times N observations as independent, which is false — errors are cross-sectionally correlated at each tt. Fama-MacBeth separates the time dimension (averaging γ^k,t\hat{\gamma}_{k,t} across periods) from the cross-section, giving standard errors that are robust to cross-sectional correlation. The cost: it ignores time-series correlation of the γ^k,t\hat{\gamma}_{k,t} series (Newey-West adjustment extends this).

L3. Critique Roll's critique of CAPM. How does the APT avoid Roll's critique? What is the distinction between a statistical factor model (PCA on returns covariance) and a fundamental factor model (sector, style exposures), and when would you use each? How would you test whether a new factor is priced versus a statistical artefact — what criteria (economic intuition, out-of-sample, Bayesian t-statistic threshold) would you apply?

Verify your understanding before moving on.

Start Quiz →