Case Study: CAPM as a Regression Model¶

Dr. Andrés García Medina¶

Email: andgarm.n@gmaiñ.com¶

Site: https://sites.google.com/view/andresgm/home¶

Model Definition

In multiple linear regression, the general model is:

$ y = \beta_0 + \beta_1 x_1 + \cdots + \beta_p x_p + \varepsilon $

where:

  • $y$: dependent variable
  • $x_i$: independent variables
  • $\beta_i$: parameters to estimate
  • $\varepsilon$: error term

The CAPM (Capital Asset Pricing Model) can be written as a linear regression model:

$ R_i - R_f = \alpha + \beta (R_m - R_f) + \varepsilon $

where:

  • $R_i$: return of the asset (GameStop)
  • $R_m$: return of the market (S&P 500)
  • $R_f$: risk-free rate
  • $\beta$: systematic risk (sensitivity to the market)
  • $\alpha$: abnormal return

Note: Sharpe, Markowitz and Merton Miller jointly received the 1990 Nobel Memorial Prize in Economic Sciences for this contribution to the field of financial economics.

Beta (β)¶

  • Measures how sensitive the asset is to market movements:

    • $ \beta = 1 $: asset moves with the market
    • $ \beta > 1 $: more volatile than the market (higher risk)
    • $ 0 < \beta < 1 $: less volatile than the market
    • $ \beta < 0 $: moves opposite to the market

Alpha (α)¶

  • Represents abnormal returns not explained by the market:

    • $ \alpha = 0 $: asset is correctly priced according to CAPM
    • $ \alpha > 0 $: asset outperforms CAPM prediction (positive abnormal return)
    • $ \alpha < 0 $: asset underperforms

Libraries¶

In [1]:
import numpy as np
import pandas as pd
import yfinance as yf
import statsmodels.api as sm
import matplotlib.pyplot as plt

Data

In [8]:
tickers = ["GME", "^GSPC"]
data = yf.download(tickers, start="2023-01-01", end="2026-03-17")
data.head()
Out[8]:
Price Close High Low Open Volume
Ticker GME ^GSPC GME ^GSPC GME ^GSPC GME ^GSPC GME ^GSPC
Date
2023-01-03 17.200001 3824.139893 19.260000 3878.459961 17.090000 3794.330078 18.639999 3853.290039 5135200 3959140000
2023-01-04 17.320000 3852.969971 17.930000 3873.159912 16.900000 3815.770020 17.250000 3840.360107 3939300 4414080000
2023-01-05 16.219999 3808.100098 17.260000 3839.739990 15.890000 3802.419922 17.059999 3839.739990 6066200 3893450000
2023-01-06 16.459999 3895.080078 16.570000 3906.189941 15.410000 3809.560059 16.000000 3823.370117 4823400 3923560000
2023-01-09 16.379999 3892.090088 17.129999 3950.570068 16.360001 3890.419922 16.650000 3910.820068 3522600 4311770000
In [4]:
data.to_excel("stock_data.xlsx")
print("Data saved to stock_data.xlsx")
Data saved to stock_data.xlsx

Compute log returns¶

In [7]:
returns = np.log(data / data.shift(1)).dropna()
returns.head()
Out[7]:
Price Close High Low Open Volume
Ticker GME ^GSPC GME ^GSPC GME ^GSPC GME ^GSPC GME ^GSPC
Date
2023-01-04 0.006952 0.007511 -0.071555 -0.001367 -0.011180 0.005635 -0.077498 -0.003361 -0.265116 0.108773
2023-01-05 -0.065617 -0.011714 -0.038084 -0.008666 -0.061624 -0.003505 -0.011076 -0.000161 0.431729 -0.125504
2023-01-06 0.014688 0.022584 -0.040798 0.017158 -0.030673 0.001876 -0.064148 -0.004272 -0.229253 0.007704
2023-01-09 -0.004872 -0.000768 0.033237 0.011297 0.059823 0.021003 0.039821 0.022615 -0.314280 0.094349
2023-01-10 0.081451 0.006954 0.054528 -0.007812 -0.006746 -0.003381 -0.021245 -0.005706 0.223041 -0.113008

Define CAPM variables¶

In [9]:
R_i = returns['Close']['GME']
R_m = returns['Close']['^GSPC']

# Risk-free rate (approx. constant daily)
Rf = 0.02 / 252  # 2% annually

# Excess returns
Y = R_i - Rf
X = R_m - Rf

Estimate regression (OLS)¶

In [10]:
# Add constant (intercept)
X = sm.add_constant(X)

model = sm.OLS(Y, X).fit()

# Results
print(model.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                    GME   R-squared:                       0.033
Model:                            OLS   Adj. R-squared:                  0.031
Method:                 Least Squares   F-statistic:                     26.84
Date:                Thu, 17 Sep 2026   Prob (F-statistic):           2.80e-07
Time:                        08:31:53   Log-Likelihood:                 1143.8
No. Observations:                 801   AIC:                            -2284.
Df Residuals:                     799   BIC:                            -2274.
Df Model:                           1                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0004      0.002     -0.197      0.843      -0.004       0.004
^GSPC          1.1356      0.219      5.181      0.000       0.705       1.566
==============================================================================
Omnibus:                      370.290   Durbin-Watson:                   1.943
Prob(Omnibus):                  0.000   Jarque-Bera (JB):            28150.260
Skew:                           1.192   Prob(JB):                         0.00
Kurtosis:                      31.944   Cond. No.                         107.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Visualization¶

In [17]:
plt.figure(figsize=(8,6))
plt.scatter(X.iloc[:,1], Y, alpha=0.5)

# Regression line
beta = model.params[1]
alpha = model.params[0]

x_vals = np.linspace(X.iloc[:,1].min(), X.iloc[:,1].max(), 100)
y_vals = alpha + beta * x_vals

plt.plot(x_vals, y_vals)

plt.xlabel("Market excess return")
plt.ylabel("GME excess return")
plt.title("CAPM: GameStop vs S&P 500")
plt.grid()
plt.show()
No description has been provided for this image