Capital Market Theory - CAPM and the Efficient Frontier › Complete CAPM Implementation
Quantitative FinanceAll chapters Capital Market Theory - CAPM and the Efficient Frontier › Complete CAPM ImplementationOne section · Sections in this chapter10 / 14 ↓
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
Let's bring together the concepts from this chapter in a worked implementation that estimates betas, constructs the Security Market Line, and calculates portfolio alphas. This implementation demonstrates how the theoretical framework translates into practical analysis tools.
import numpy as np
from scipy import stats
class CAPMAnalysis:
"""
Complete CAPM analysis toolkit for portfolio and security analysis.
"""
def __init__(self, returns_df, market_returns, rf_rate):
"""
Initialize with return data.
Parameters:
-----------
returns_df : pd.DataFrame
Asset returns with assets as columns
market_returns : pd.Series
Market index returns
rf_rate : float
Risk-free rate (same frequency as returns)
"""
self.returns = returns_df
self.market_returns = market_returns
self.rf = rf_rate
self.excess_returns = returns_df.subtract(rf_rate, axis=0)
self.market_excess = market_returns - rf_rate
self.betas = {}
self.alphas = {}
self.r_squared = {}
def estimate_betas(self):
"""Estimate beta for each asset using OLS regression."""
for asset in self.returns.columns:
slope, intercept, r_value, _, _ = stats.linregress(
self.market_excess, self.excess_returns[asset]
)
self.betas[asset] = slope
self.alphas[asset] = intercept
self.r_squared[asset] = r_value**2
return self.betas
def get_capm_expected_return(self, beta):
"""Calculate CAPM expected return for a given beta."""
market_premium = self.market_returns.mean() - self.rf
return self.rf + beta * market_premium
def security_market_line(self, beta_range=None):
"""Generate Security Market Line data."""
if beta_range is None:
beta_range = np.linspace(0, 2, 100)
market_premium = self.market_returns.mean() - self.rf
expected_returns = self.rf + beta_range * market_premium
return beta_range, expected_returns
def portfolio_beta(self, weights):
"""Calculate portfolio beta as weighted average."""
if not self.betas:
self.estimate_betas()
beta_values = [self.betas[asset] for asset in self.returns.columns]
return np.dot(weights, beta_values)
def tracking_error(self, weights, target_beta=1.0):
"""Calculate deviation from target beta."""
return abs(self.portfolio_beta(weights) - target_beta)With the CAPMAnalysis class defined, we generate a synthetic dataset representing different sectors to demonstrate the workflow. The simulation creates returns that follow the CAPM structure, with each sector having a distinct beta and alpha reflecting its risk characteristics and potential mispricing.
# Generate realistic simulated data for demonstration
np.random.seed(2024)
n_periods_full = 60 # 5 years monthly
# Market returns
market_ret = np.random.normal(0.008, 0.04, n_periods_full)
# Asset characteristics
assets = {
"Tech_Growth": {"beta": 1.4, "alpha": 0.003, "idio_vol": 0.06},
"Large_Value": {"beta": 0.9, "alpha": 0.001, "idio_vol": 0.03},
"Utilities": {"beta": 0.5, "alpha": 0.0005, "idio_vol": 0.02},
"Healthcare": {"beta": 0.7, "alpha": 0.002, "idio_vol": 0.035},
"Financials": {"beta": 1.2, "alpha": -0.001, "idio_vol": 0.05},
"Consumer": {"beta": 0.85, "alpha": 0.0015, "idio_vol": 0.03},
"Energy": {"beta": 1.1, "alpha": -0.002, "idio_vol": 0.055},
"Real_Estate": {"beta": 0.95, "alpha": 0.001, "idio_vol": 0.04},
}
rf_monthly = 0.003 # ~3.6% annual
# Generate returns
returns_data = {}
for name, params in assets.items():
idio = np.random.normal(0, params["idio_vol"], n_periods_full)
returns_data[name] = (
rf_monthly
+ params["alpha"]
+ params["beta"] * (market_ret - rf_monthly)
+ idio
)
returns_df = pd.DataFrame(returns_data)
market_series = pd.Series(market_ret, name="Market")We initialize the analysis with our returns data and estimate the beta for each asset. The estimation process runs separate regressions for each sector, extracting the slope coefficient as the beta estimate.
# Run CAPM analysis
capm = CAPMAnalysis(returns_df, market_series, rf_monthly)
capm.estimate_betas()
# Create summary DataFrame
summary_data = []
for asset in returns_df.columns:
actual_ret = returns_df[asset].mean()
expected_ret = capm.get_capm_expected_return(capm.betas[asset])
summary_data.append(
{
"Asset": asset,
"Beta": capm.betas[asset],
"R²": capm.r_squared[asset],
"Actual Return": actual_ret * 12, # Annualized
"CAPM Expected": expected_ret * 12, # Annualized
"Alpha (Annual)": capm.alphas[asset] * 12,
}
)
summary_df = pd.DataFrame(summary_data)Finally, we display the summary statistics to compare the actual and expected returns.
CAPM Analysis Summary
=====================================================================================
Asset Beta R² Actual Return CAPM Expected Alpha (Annual)
Tech_Growth 1.516 0.500 0.164 0.170 -0.006
Large_Value 0.755 0.439 0.196 0.103 0.093
Utilities 0.539 0.504 0.054 0.084 -0.030
Healthcare 0.843 0.451 -0.008 0.111 -0.119
Financials 1.351 0.524 0.112 0.156 -0.044
Consumer 0.870 0.503 0.156 0.113 0.043
Energy 1.208 0.351 0.062 0.143 -0.081
Real_Estate 1.087 0.485 0.169 0.132 0.037The summary table displays the key metrics for each asset. 'Tech_Growth' and 'Financials' exhibit betas above 1.0, indicating they carry more systematic risk than the market. Conversely, 'Utilities' has a beta of 0.5, confirming its defensive nature. The alpha values show the divergence between actual and expected returns; positive alphas suggest potential undervaluation or superior performance adjusted for risk. The R-squared values indicate how much of each sector's return variation is explained by market movements, with higher values suggesting greater systematic risk exposure relative to idiosyncratic risk.
We can also visualize these results on the Security Market Line to see the alphas graphically.

The analysis reveals several patterns consistent with CAPM theory. Higher-beta assets like Tech_Growth and Financials show higher expected returns, while defensive sectors like Utilities exhibit lower betas and returns. The spread of assets around the SML represents alpha, either skill/mispricing or compensation for risks not captured by CAPM. This visualization makes it easy to identify which sectors are outperforming or underperforming their risk-adjusted benchmarks, and gives actionable insights for portfolio construction.
The R-squared values from our CAPM analysis reveal how much of each sector's return variation is explained by market movements. We can visualize these values to compare systematic versus idiosyncratic risk across sectors.
