Principal Component Analysis and Factor Extraction › PCA Applied to Equity Returns
Quantitative FinanceAll chapters Principal Component Analysis and Factor Extraction › PCA Applied to Equity ReturnsOne section · Sections in this chapter6 / 13 ↓
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
Equity returns present a more challenging application of PCA. Unlike yield curves, where maturities have a natural ordering and the factor structure is highly regular, equity returns exhibit a messier factor structure with lower variance concentration in the leading components.
Analyzing Sector Returns
Let's apply PCA to a set of simulated sector returns:
import numpy as np
import pandas as pd
np.random.seed(456)
# Define sectors
sectors = [
"Technology",
"Healthcare",
"Financials",
"Consumer",
"Industrials",
"Energy",
"Materials",
"Utilities",
"Real Estate",
"Communications",
]
n_sectors = len(sectors)
n_days = 500
# Simulate factor structure:
# Factor 1: Market factor (affects all sectors)
# Factor 2: Growth vs Value (Tech/Healthcare vs Financials/Energy)
# Factor 3: Interest rate sensitivity (Utilities/Real Estate vs others)
market_factor = np.random.randn(n_days)
growth_value_factor = np.random.randn(n_days)
rate_sensitivity_factor = np.random.randn(n_days)
# Define factor exposures (betas) for each sector
market_betas = np.array([1.2, 0.9, 1.1, 1.0, 1.05, 1.3, 1.1, 0.5, 0.8, 1.15])
gv_betas = np.array([0.8, 0.5, -0.6, 0.2, 0.0, -0.7, -0.3, -0.2, 0.1, 0.4])
rate_betas = np.array([-0.2, 0.1, 0.4, 0.0, 0.1, 0.2, 0.1, 0.7, 0.6, -0.1])
# Generate returns
returns = np.zeros((n_days, n_sectors))
for i in range(n_sectors):
returns[:, i] = (
market_betas[i] * market_factor * 0.01
+ gv_betas[i] * growth_value_factor * 0.005
+ rate_betas[i] * rate_sensitivity_factor * 0.004
+ np.random.randn(n_days) * 0.008
) # Idiosyncratic
returns_df = pd.DataFrame(returns, columns=sectors)# Apply PCA to sector returns
returns_centered = returns - returns.mean(axis=0)
cov_returns = np.cov(returns_centered, rowvar=False)
eigenvalues_ret, eigenvectors_ret = np.linalg.eigh(cov_returns)
sort_idx = np.argsort(eigenvalues_ret)[::-1]
eigenvalues_ret = eigenvalues_ret[sort_idx]
eigenvectors_ret = eigenvectors_ret[:, sort_idx]
# Calculate variance metrics
total_var_ret = eigenvalues_ret.sum()
explained_pct_ret = 100 * eigenvalues_ret / total_var_ret
cumulative_pct_ret = np.cumsum(explained_pct_ret)Variance Explained by Principal Components (Sector Returns): ------------------------------------------------------- PC1: 64.47% (Cumulative: 64.47%) PC2: 6.36% (Cumulative: 70.83%) PC3: 4.14% (Cumulative: 74.97%) PC4: 4.08% (Cumulative: 79.05%) PC5: 3.85% (Cumulative: 82.90%)
Unlike yield curves, equity returns require more components to achieve high variance explanation. The first component (the market factor) explains a smaller fraction of total variance because idiosyncratic risk is more significant for individual stocks and sectors.





The loading patterns reveal the underlying factor structure:
- PC1: All positive loadings indicate a common market factor. The relative magnitudes reflect each sector's market beta.
- PC2: The pattern of positive and negative loadings separates sectors along a dimension that might correspond to growth versus value characteristics.
- PC3: Further differentiation, potentially capturing interest rate sensitivity given the high loadings on Utilities and Real Estate.