How To Calculate And Test The CAPM With Python

Joe Godot
6 min readFeb 17, 2023

--

Introduction

Investors use asset pricing theories to evaluate whether an asset is over- or undervalued. One of the most popular asset pricing theories is the Capital Asset Pricing Model (CAPM), which measures a stock’s expected return based on its beta. Beta is a measure of a stock’s volatility relative to the market. In this report, we assess how standard asset pricing theories work in Italy, focusing on the top ten stocks on the Italian market. We will explore how betas, beta dispersion, and cross-sectional regressions impact asset pricing in Italy.

Data and Methodology

We obtained data from Yahoo Finance for the ten largest stocks in Italy over the past five years (January 2017 to January 2022). The suitable market index for developing the analysis is the STOXX Euro 600, which represents large, medium, and small capitalization companies in 18 European countries, including Italy. The risk-free rate selected is the Italy 3-month Government Bond.

start, end = '2017-1-1', '2022-1-1'
ticker = '^STOXX'
index = yf.download(ticker, start, end)
index_close = index['Adj Close']
average_market_ret = index_close.pct_change().mean() * 21
rf = pd.read_csv('wbond.csv')
rf_close = rf.Ultimo / 100

To calculate betas, we used two methods: the covariance method: β = (Cov (ri, rm))/(Var (rm)) and a time-series regression with the formula: r(i, t) = αi + βi r(m, t) + u(i, t). We also computed beta dispersion, which measures the range of beta values among a group of stocks. We then ran a cross-sectional regression to evaluate how the various factors impact stock prices.

def beta_calculator(ticker, ticker1, start, end):
index = yf.download(ticker, start, end, progress = False)
index_close = index['Adj Close']
index_log_return = np.log(index_close / index_close.shift(1))
stock = yf.download(ticker1, start, end, progress = False)
stock_close = stock['Adj Close']
stock_log_ret = np.log(stock_close / stock_close.shift(1))
frame = {'Index': index_log_return, 'Stock': stock_log_ret}
df = pd.DataFrame(frame).fillna(method = 'bfill')
matrix_cov = df.cov()
cov_index_stock = matrix_cov['Stock'][0]
market_var = index_log_return.var()
print(ticker1, ticker)
print('Beta: ', cov_index_stock / market_var)
return cov_index_stock / market_var

def regression(index, stock, start, end):
index = yf.download(index, start, end, progress = False)
index_ret = index['Adj Close'].pct_change()
stock = yf.download(stock, start, end, progress = False)
stock_ret = stock['Adj Close'].pct_change()
frame = {'Index': index_ret, 'Stock': stock_ret}
df = pd.DataFrame(frame).dropna()
y = df.Stock
X = sm.add_constant(df.Index)
OLS_regr = sm.OLS(y, X).fit()
print(OLS_regr.pvalues)
print(OLS_regr.params[1])
return OLS_regr.params[1]

We then create a dataframe in which to store the results of our computations.

dftot = pd.DataFrame()
dftot.index = stocks
dftot['regression_betas'] = regression_betas
dftot['formula_betas'] = betas

Output:
regression_betas formula_betas
STLA.MI 1.552083 1.555609
ENEL.MI 0.949989 0.962650
ENI.MI 1.307908 1.316362
ISP.MI 1.295006 1.287798
RACE.MI 0.987214 0.974808
STM.MI 1.609396 1.595468
G.MI 0.945664 0.944447
UCG.MI 1.458870 1.455224
CNHI.MI 1.537912 1.542607
ATL.MI 1.219665 1.255490

We found that seven of the top ten stocks in Italy have a beta higher than one, indicating they are more volatile or risky than the broader market. The remaining three stocks had a beta very close to one. Investors with a high risk tolerance and investment goals may prefer high beta stocks since they offer the potential for higher returns.

CAPM test

The next step of the analysis was running a cross-sectional regression to formally test the CAPM. The Capital Asset Pricing Model (CAPM) describes the relationship between the systematic risk and the expected return for assets. This model is based on the relation between the asset’s beta, the risk-free rate (Italian 3 months BTP in this analysis) and the equity risk premium. First of all, the team has computed the expected return E(Ri) of the stocks using the CAPM’s formula:

E(Ri) = RF + βi (E(RM) - RF)

Where:

  • RF = risk free rate
  • RM = return on market portfolio
  • βi = beta, index of systematic risk (calculated as mentioned before in the report)

Then, we run the cross-sectional regression estimated as follows:

mean(ri - rf)= γ0 + γ1 βi + 𝜖
where γ0 is the intercept and γ1 the coefficient of beta

using the average (over time) equity premium as the dependent variable and the beta as the independent variable.

mean_ret_beta = []
monthly_ret = []
mean_ri_rf = []
for i in range(len(stocks)):
stock = yf.download(stocks[i], start, end, progress = False)
stock_close = stock['Adj Close'].resample('1W').last()
for j in range(len(stock_close)):
li = []
ri_rf = stock_close.pct_change()[j] - rf_close[j]
li.append(ri_rf)
mean_ri_rf.append((sum(li)/len(li)) * 4)
mean_ret_beta.append((regression_betas[i], stock_close.pct_change().mean() * 4))
monthly_ret.append(stock_close.pct_change().mean() * 4)

dftot['mean ri-rf'] = mean_ri_rf
dftot['m_expected_returns'] = exp_ret_beta
dftot['m_mean_returns'] = monthly_ret
y = dftot['mean ri-rf']
X = sm.add_constant(dftot.regression_betas)
OLS_regr = sm.OLS(y, X).fit()
OLS_regr.summary()

The result has been plotted in a graph in order to better visualize the result.

If the CAPM is valid, γ0 should be equal to zero and γ1 to rM — rf . However, the value of γ0 is equal to 0.0530, but statistically insignificant even at 10% level and the value of γ1 is 0.0024, which is different from the excess market return. Overall, our regression estimates suggest that standard CAPM is not able to provide the results which could validate it. The returns of the period taken into consideration, however, have been influenced by the Covid-19 pandemic, which led to a crash of the stock market in March 2020.

Then, we plot the theoretical Security Market Line (SML) which is the graphical representation of the CAPM. The Y-intercepts is equal to the risk-free interest rate, while the slope is equal to the market risk premium and reflects the risk return tradeoff at a given time.

x1, y1 = 0, rf
x2, y2 = 1, average_market_ret
plt.axline((x1, y1), (x2, y2));
plt.plot(x1, y1, x2, y2, marker='o', color='red');
plt.scatter(*zip(*mean_ret_beta), marker='o')
plt.xlabel('Beta')
plt.show()

Looking at the results that we obtained during the analysis, the SML generated is different from the theoretical one and differences are clearly highlighted in this graph.

Comparison between the obtained and the theoretical SML

The intercept is not consistent with the theoretical prediction. Indeed, the CAPM intercept in the theoretical prediction is lower, and the slope is steeper than what it is according to the team’s results.

According to the outcomes obtained from the CAPM model we can not affirm that there is a linear relationship between expected return and market risk, as shown by the low R-squared value. The price of risk is not consistent with the prediction of CAPM, in this case we would have had a linear relationship.

This discrepancy could be caused by the fact that the Italian economy has been facing several challenges in recent years, including high levels of debt and slow economic growth, which would potentially lead to increased risk for the stocks and, therefore, higher expected return than predicted by the CAPM. Additionally, the ten largest stocks in Italy are primarily in the financial and industrial sectors, which are vulnerable to economic downturns and, hence, more risky.

Small but relevant caveat

There are a few issues with the analysis we need to consider. First, the period chosen for the analysis includes a major market shock, which could mess up our results. Second, the sample of stocks used in the analysis might be too small. This could affect the reliability of our findings.

On top of that, testing the CAPM (a financial model used to price assets) is tricky business. The market index used in the analysis isn’t exactly the “market portfolio” used in the CAPM. This, combined with the fact that security betas are estimated with a fair bit of error, can make it difficult to get accurate results.

To make things even more complicated, many researchers have criticized the CAPM. For example, Roll (who’s quite famous in this area) has pointed out some problems with the model. Fama and French have also criticized the CAPM, noting that it fails to capture certain anomalies related to the size and book-to-market value of company stocks.

Finally, the researchers made a critical assumption that the Covariance matrix is driven by economic factors. This is something we need to consider when looking at the results.

Overall, I wouldn’t advise to rely too much on the results obtained by using the CAPM model since it doesn’t seem able to estimate precisely the expected return of an asset and has many problems.

--

--

Joe Godot
Joe Godot

Written by Joe Godot

Statistics, machine learning and quantitative finance connoisseur, programming lover, and humanities aficionado.

No responses yet