PCA of the Yield Curve: Level Slope Curvature
Principal-component analysis (PCA) rotates correlated yield-curve changes into orthogonal factors ranked by explained variance. In major rates markets, the leading components often resemble level, slope, and curvature, but that familiar label is an empirical result of a chosen sample, grid, and normalization—not a law of term-structure economics.
Build the data matrix correctly
Let X contain daily changes in zero rates or par swap rates: rows are dates and columns are selected maturities. PCA diagonalizes the sample covariance matrix:
Σ = (XᵀX)/(T - 1) = V diag(λ) Vᵀ
factor_score_t = X_t V
Center each tenor before estimating Σ; otherwise PCA mainly describes the average curve. Use rate changes rather than levels because levels are non-stationary, and maintain a stable tenor grid through time. A zero-rate grid derived from the same curve construction is usually cleaner than raw bond yields. See yield-curve bootstrapping for the distinction.
| Design choice | Why it matters | Common mistake |
|---|---|---|
| zero versus par rates | changes factor loadings | mixing quote types |
| daily versus weekly moves | changes noise and regimes | treating results as invariant |
| unweighted covariance | describes rate variance | calling it portfolio risk |
| DV01-weighted covariance | aligns factors to P&L | losing simple rate interpretation |
Estimate and orient components
Eigenvectors have arbitrary sign. Flip each loading vector to an agreed convention—perhaps positive long-end loading for level—before reporting factor returns. Otherwise a positive “level” shock may change sign after every refit.
import numpy as np
def yield_pca(rate_changes, n_components=3):
X = np.asarray(rate_changes)
X = X - X.mean(axis=0, keepdims=True)
cov = np.cov(X, rowvar=False)
eigval, eigvec = np.linalg.eigh(cov)
order = np.argsort(eigval)[::-1]
eigval, eigvec = eigval[order], eigvec[:, order]
loadings = eigvec[:, :n_components]
# Convention: positive 10Y/last-tenor loading for every factor.
loadings *= np.sign(loadings[-1, :])
return loadings, eigval[:n_components], X @ loadings
The first component is generally positive across maturities: a parallel-like level move. The second usually has opposite short- and long-end signs: slope. The third normally alternates signs: curvature. Yet a crisis sample can promote front-end policy shocks, and a single-country curve can differ sharply from a cross-currency panel.
| Component | Typical loading shape | Desk interpretation |
|---|---|---|
| PC1 | same sign across tenors | duration / level risk |
| PC2 | short and long opposite | 2s10s or 5s30s steepening |
| PC3 | belly opposite wings | butterfly / curvature |
From statistical factor to hedge
For an instrument with key-rate DV01 vector d, approximate its sensitivity to a PCA factor loading vk by dᵀvk. To neutralize several factors, stack instrument exposure vectors into A and solve a constrained least-squares hedge:
min_h || b + A h ||² + γ ||h||²
The ridge term γ prevents huge offsetting hedge notionals when candidate hedges are nearly collinear. Use actual instrument PV changes to validate the linear approximation; duration and convexity explains why large moves, callable bonds, and changing cash flows violate it.
PCA factors are orthogonal in the historical covariance metric, not necessarily uncorrelated in future stressed markets. A factor-neutral book can retain substantial key-rate loss if a realized move falls outside the first three components. Report residual exposure to the omitted subspace and run explicit curve-twist shocks.
Stability, regimes, and leakage
Estimate rolling PCA and compare subspaces rather than individual vectors alone. A useful stability statistic is the principal angle between the old and new leading-factor spaces. If it widens, historical factor hedges are stale even where explained variance remains high.
Do not select components using future data in a backtest. Refit using only information available on each rebalance date, apply that basis forward, and include transaction costs from factor hedge turnover. A “level” factor with high explained variance is not automatically a return factor; expected returns require a separate economic hypothesis, such as carry, policy repricing, or supply-demand pressure.
Key takeaways
- PCA summarizes correlated curve changes but depends on the data definition and estimation window.
- Orient eigenvectors and use a fixed tenor grid to make factor reports comparable through time.
- Translate loadings into P&L with key-rate DV01s, then validate with full repricing.
- Factor neutrality does not eliminate risks in omitted directions, nonlinear products, or regime shifts.
- Refit without look-ahead and measure hedge turnover as part of any PCA strategy.
