5  Capital Asset Pricing Model

TipLearning objectives

After this lecture you should be able to

  • derive the CAPM regression equation from the CAPM formula,
  • interpret the intercept and slope of the fitted regression in financial terms, i.e. excess return and beta,
  • fit the CAPM model in R using weekly returns downloaded from Yahoo Finance, and
  • evaluate a fitted CAPM model by comparing its \(R^2\) and estimated beta to those of another asset.

The Capital Asset Pricing Model (CAPM) is a widely used model in finance that describes the relationship between systematic risk and expected return for assets, particularly stocks. The CAPM formula is given by: \[E[R] = R_f + \beta (E[R_m] - R_f)\] where

The return of the asset is calculated based on the current and previous price of the asset. If the price at time \(t\) is \(P_t\) and the price at time \(t-1\) is \(P_{t-1}\), then the return of the asset at time \(t\) is given by: \[R_t = \frac{P_t - P_{t-1}}{P_{t-1}}.\]

To fit a simple linear regression model to the CAPM, we can rewrite the equation in the form of a linear regression model: \[\underbrace{R_t - R_{t,f}}_{\text{response}} = \beta_0 + \beta_1 \underbrace{(R_{t,m} - R_{t,f})}_{\text{explanatory}} + \epsilon_t\] where, at time \(t\),

Taking expectations of both sides and substituting the CAPM formula for \(E[R_t]\) and \(E[R_{t,m}]\),

\[E[R_t - R_{t,f}] = \beta_1 (E[R_{t,m}] - R_{t,f}),\]

which matches the regression equation above only if \(\beta_0 = 0\). CAPM is therefore making a testable prediction about the intercept, not just the slope.

The coefficient interpretations in this model are

5.1 Example: AAPL vs SPY

We fit the CAPM using Apple (AAPL) as the asset, the S&P 500 ETF (SPY) as the market portfolio, and the 13-week T-bill yield (^IRX) as the risk-free rate.

library("tidyverse")
library("quantmod")

theme_set(theme_bw())

# ------------------------------------------------------------------------------
# Get data
# ------------------------------------------------------------------------------

# Define date range
start_date <- "2000-01-01"
end_date <- Sys.Date()

# Download Equity data from Yahoo Finance
getSymbols("AAPL", from = start_date, to = end_date, auto.assign = TRUE)
getSymbols("SPY", from = start_date, to = end_date, auto.assign = TRUE)
getSymbols("^IRX", from = start_date, to = end_date, auto.assign = TRUE) # 13-Week T-Bill Yield Index

# Calculate weekly returns for equity tickers
aapl_weekly <- weeklyReturn(AAPL, type = "arithmetic")
spy_weekly <- weeklyReturn(SPY, type = "arithmetic")

# Process the T-Bill (^IRX) data
# Extract the closing yield values matching our equity weekly date index
# Using Ad() gets the adjusted close, or Cl() for regular close
t_bill_weekly_yield <- Cl(IRX)[index(spy_weekly)]

# Forward-fill any missing data gap days (like market holidays)
t_bill_weekly_yield <- na.locf(t_bill_weekly_yield, na.rm = TRUE)

# Convert annualized yield percentage to weekly decimal: (Yield / 100) / 52
t_bill_weekly_return <- (t_bill_weekly_yield / 100) / 52

# Merge all series into one object and rename columns
merged_returns <- merge(aapl_weekly, spy_weekly, t_bill_weekly_return)
colnames(merged_returns) <- c(
  "AAPL_Return",
  "SPY_Return",
  "TBill_Weekly_Yield"
)

# Convert the xts object to a standard data.frame
d <- data.frame(Date = index(merged_returns), coredata(merged_returns)) |>
  na.omit() |>
  remove_rownames() |>
  mutate(
    AAPL_Excess_Return = AAPL_Return - TBill_Weekly_Yield,
    SPY_Excess_Return = SPY_Return - TBill_Weekly_Yield
  )

# ------------------------------------------------------------------------------
# Exploratory Data Analysis (EDA)
# ------------------------------------------------------------------------------

scatter_plot <- ggplot(d, aes(x = SPY_Excess_Return, y = AAPL_Excess_Return)) +
  geom_point(alpha = 0.5) +
  labs(
    title = "Scatter Plot of AAPL vs SPY Weekly Returns",
    x = "SPY Weekly Excess Return",
    y = "AAPL Weekly Excess Return"
  )

scatter_plot

# Hexbin plot
hexbin_plot <- ggplot(d, aes(x = SPY_Excess_Return, y = AAPL_Excess_Return)) +
  geom_hex(bins = 30) +
  labs(
    title = "Hexbin Plot of AAPL vs SPY Weekly Returns",
    x = "SPY Weekly Excess Return",
    y = "AAPL Weekly Excess Return"
  ) +
  scale_fill_gradient(low = "lightblue", high = "darkblue")

hexbin_plot

hexbin_smooth_plot <- hexbin_plot + geom_smooth(method = "lm", formula = y ~ x)
hexbin_smooth_plot

# ------------------------------------------------------------------------------
# Fit the CAPM model using simple linear regression
# ------------------------------------------------------------------------------

# Fit the linear regression model: AAPL excess return ~ SPY excess return
capm_model <- lm(AAPL_Excess_Return ~ SPY_Excess_Return, data = d)

# View the summary of the CAPM model
summary(capm_model)

coef(capm_model)
confint(capm_model)
summary(capm_model)$r.squared


Call:
lm(formula = AAPL_Excess_Return ~ SPY_Excess_Return, data = d)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.49795 -0.02051 -0.00147  0.02132  0.25639 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)       0.003956   0.001136   3.483 0.000512 ***
SPY_Excess_Return 1.094719   0.046198  23.696  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.04237 on 1392 degrees of freedom
Multiple R-squared:  0.2874,    Adjusted R-squared:  0.2869 
F-statistic: 561.5 on 1 and 1392 DF,  p-value: < 2.2e-16
      (Intercept) SPY_Excess_Return 
      0.003956031       1.094718953 
                        2.5 %      97.5 %
(Intercept)       0.001727829 0.006184234
SPY_Excess_Return 1.004093509 1.185344397
[1] 0.2874361

5.2 Class activity

Find a stock, or an index, whose CAPM fit against SPY has

  • a higher \(R^2\) than AAPL’s, and
  • a more extreme beta, i.e. farther from 1 in either direction, than AAPL’s.

Adapt the code above by substituting your chosen ticker for AAPL (getSymbols() accepts any Yahoo Finance ticker), re-fit the model, and report the resulting \(R^2\) and \(\hat\beta_1\) alongside AAPL’s for comparison.