Skip to content

Using PCA to Find Common Portfolio Risk Factors

Original question

How do I use principal component analysis to identify common risk factors across a portfolio of financial assets?
Fit principal component analysis (PCA) to aligned asset returns. Each principal component is an orthogonal direction through the return data, ranked by the variance it captures. Inspect the direction's asset coefficients to interpret the factor, then calculate how much of your portfolio's variance comes from its exposure to each component.

Read the direction and its variance separately

For centered asset returns rr with covariance matrix Σ\Sigma, write Σ=UΛU⊤\Sigma=U\Lambda U^\top. The columns uju_j of UU are orthonormal eigenvectors, and the diagonal entries λj\lambda_j of Λ\Lambda are ordered from largest to smallest. The score fj=uj⊤rf_j=u_j^\top r has sample variance λj\lambda_j, and scores from different components have sample covariance zero.

The share of the universe's total variance explained by component jj is λj/∑iλi\lambda_j/\sum_i\lambda_i. The variance contribution to a portfolio with weights ww is

λj(uj⊤w)2.\lambda_j(u_j^\top w)^2.
These contributions sum to w⊤Σww^\top\Sigma w when you retain all components. With correlation PCA, r=Dzr=Dz, where DD contains the asset standard deviations. Use DwDw as the exposure vector in standardised space so the result remains in portfolio-return units.

A market-like component from three correlated stocks

Suppose three standardised equity returns have pairwise correlation 0.60. Their first eigenvector is (1,1,1)⊤/3(1,1,1)^\top/\sqrt 3 with eigenvalue 2.20. The remaining two eigenvalues are 0.40 each. The first component therefore explains 73.3% of total standardised asset variance. With equal asset volatilities and equal capital weights, the portfolio is exactly aligned with that first component and it accounts for 100% of the portfolio's modelled variance. That distinction matters when using PCA to explain an allocation. The asset universe can have meaningful regional factors even when your chosen weights give them little exposure.

A component's sign is arbitrary. Multiplying the loading vector and every score by minus one leaves the fit unchanged. If you compare neighbouring windows, orient a market-like direction the same way before interpreting a sign flip. With tied or nearly tied eigenvalues, individual vectors can also rotate inside the corresponding subspace. In the three-asset example the two smaller eigenvalues are tied, so it is more meaningful to interpret their joint span than to give the second and third directions separate economic names.

Fit PCA on a fixed training window

import numpy as np from sklearn.decomposition import PCA rng = np.random.default_rng(8) market = rng.normal(size=(1_000, 1)) noise = rng.normal(size=(1_000, 3)) returns = (np.sqrt(0.6) * market + np.sqrt(0.4) * noise) returns *= np.array([0.010, 0.013, 0.017]) train, test = returns[:750], returns[750:] center, scale = train.mean(axis=0), train.std(axis=0, ddof=1) pca = PCA().fit((train - center) / scale) future_scores = pca.transform((test - center) / scale) weights = np.array([0.5, 0.3, 0.2]) exposure = pca.components_ @ (scale * weights) risk_by_component = pca.explained_variance_ * exposure**2 assert np.allclose(risk_by_component.sum(), np.var(train @ weights, ddof=1)) print(pca.explained_variance_ratio_.round(3)) print((risk_by_component / risk_by_component.sum()).round(3))

References and next steps