{
"cells": [
{
"cell_type": "markdown",
"id": "f71d16cf-9782-4cb3-bbda-09ccbdc92a66",
"metadata": {},
"source": [
"# Estimating beta via CAPM, using Yahoo Finance\n",
"\n",
"Let's estimate, for each firm, for each year, the alpha and beta of a stock, using CAPM as our asset pricing model.\n",
"\n",
"So we want a dataset that looks like this:\n",
"\n",
"| Firm | Year | alpha | beta | \n",
"| --- | --- | --- | --- |\n",
"| GM | 2000 | 0.01 | 1.04 |\n",
"| GM | 2001 | -0.005 | 0.98 |\n",
"\n",
"...but it will do this for every firm, every year!\n",
"\n",
"The psuedocode is relatively simple:\n",
"\n",
"1. Load the data on stock returns.\n",
"2. Load the market return premium and the risk-free rate (the \"factors\" in CAPM). \n",
" - Note: Make sure your returns are scaled like your factors (e.g., below, I convert stock returns to percentages to match the convention in the FF dataset)\n",
" - Make sure your stock returns and market returns are both excess returns\n",
"3. Merge the stock and factors returns \n",
"3. For each firm and time period you're interested in, estimate the CAPM model to get the stock's alpha and beta for that period.\n",
"\n",
"```{note}\n",
"1. This page assumes you are familiar with CAPM, the capital asset pricing model. \n",
"2. This page uses Yahoo Finance for stock returns. Yahoo Finance is more of a \"quick and dirty\" way to get return data. CRSP is the industry standard data provider for stock returns, and you can access via WRDS.\n",
"3. This file can estimate CAPM for any firms that Yahoo has ticker data for.\n",
"4. As written, it will estimate beta separately _for each calendar year_ in the date range you give it. But you can adjust that.\n",
"```\n"
]
},
{
"cell_type": "markdown",
"id": "6978b8e6-8741-44d2-9639-1314ac72b311",
"metadata": {},
"source": [
"## Getting started\n",
"\n",
"```{note}\n",
"The code below might need to be modified to work as of Feb 2023. [The fix is here.](https://github.com/LeDataSciFi/ledatascifi-2023/issues/6)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "5e589e7c-02d3-458a-8749-5e1db8c1f1be",
"metadata": {},
"outputs": [],
"source": [
"#!pip install pandas_datareader # uncomment and run this ONE TIME ONLY to install pandas data reader"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "bb3d1e87-288f-42a8-9a97-7146a34d102d",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import pandas_datareader as pdr # you might need to install this (see above)\n",
"from datetime import datetime"
]
},
{
"cell_type": "markdown",
"id": "5dcfeb71-baea-44ee-a52b-679fac6c5a20",
"metadata": {
"tags": []
},
"source": [
"## Load asset return data \n",
"\n",
"Load your stock returns. This file uses yahoo finance.\n",
"The returns don't even have to be firms! They can be any asset. (Portfolios, mutual funds, crypto, …)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1461aebe-dc3e-413d-b068-aa2ac1e6e93e",
"metadata": {},
"outputs": [],
"source": [
"# choose your firms and years \n",
"stocks = ['SBUX','AAPL','MSFT']\n",
"start = datetime(2016, 1, 1)\n",
"end = datetime(2016, 12, 31)"
]
},
{
"cell_type": "markdown",
"id": "322acfe6-4cf0-43ff-ae6d-f4a8c8d09c70",
"metadata": {},
"source": [
"```{tip}\n",
"The code in the next block is explained more thoroughly in `handouts/factor_loading_simple.ipynb` in the textbook repo because that file prints the status of the data throughout. Looking at this might help.\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5015249e-9bcf-4fc4-9914-23dbfe91c201",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" Firm | \n",
" Date | \n",
" Adj Close | \n",
" ret | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" AAPL | \n",
" 2016-01-04 | \n",
" 24.220573 | \n",
" NaN | \n",
"
\n",
" \n",
" 1 | \n",
" AAPL | \n",
" 2016-01-05 | \n",
" 23.613630 | \n",
" -2.505899 | \n",
"
\n",
" \n",
" 2 | \n",
" AAPL | \n",
" 2016-01-06 | \n",
" 23.151514 | \n",
" -1.956989 | \n",
"
\n",
" \n",
" 3 | \n",
" AAPL | \n",
" 2016-01-07 | \n",
" 22.174414 | \n",
" -4.220460 | \n",
"
\n",
" \n",
" 4 | \n",
" AAPL | \n",
" 2016-01-08 | \n",
" 22.291668 | \n",
" 0.528782 | \n",
"
\n",
" \n",
" 5 | \n",
" AAPL | \n",
" 2016-01-11 | \n",
" 22.652626 | \n",
" 1.619251 | \n",
"
\n",
" \n",
" 6 | \n",
" AAPL | \n",
" 2016-01-12 | \n",
" 22.981384 | \n",
" 1.451303 | \n",
"
\n",
" \n",
" 7 | \n",
" AAPL | \n",
" 2016-01-13 | \n",
" 22.390528 | \n",
" -2.571022 | \n",
"
\n",
" \n",
" 8 | \n",
" AAPL | \n",
" 2016-01-14 | \n",
" 22.880220 | \n",
" 2.187053 | \n",
"
\n",
" \n",
" 9 | \n",
" AAPL | \n",
" 2016-01-15 | \n",
" 22.330753 | \n",
" -2.401494 | \n",
"
\n",
" \n",
" 10 | \n",
" AAPL | \n",
" 2016-01-19 | \n",
" 22.222696 | \n",
" -0.483893 | \n",
"
\n",
" \n",
" 11 | \n",
" AAPL | \n",
" 2016-01-20 | \n",
" 22.252586 | \n",
" 0.134502 | \n",
"
\n",
" \n",
" 12 | \n",
" AAPL | \n",
" 2016-01-21 | \n",
" 22.139933 | \n",
" -0.506250 | \n",
"
\n",
" \n",
" 13 | \n",
" AAPL | \n",
" 2016-01-22 | \n",
" 23.317051 | \n",
" 5.316720 | \n",
"
\n",
" \n",
" 14 | \n",
" AAPL | \n",
" 2016-01-25 | \n",
" 22.861835 | \n",
" -1.952286 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Firm Date Adj Close ret\n",
"0 AAPL 2016-01-04 24.220573 NaN\n",
"1 AAPL 2016-01-05 23.613630 -2.505899\n",
"2 AAPL 2016-01-06 23.151514 -1.956989\n",
"3 AAPL 2016-01-07 22.174414 -4.220460\n",
"4 AAPL 2016-01-08 22.291668 0.528782\n",
"5 AAPL 2016-01-11 22.652626 1.619251\n",
"6 AAPL 2016-01-12 22.981384 1.451303\n",
"7 AAPL 2016-01-13 22.390528 -2.571022\n",
"8 AAPL 2016-01-14 22.880220 2.187053\n",
"9 AAPL 2016-01-15 22.330753 -2.401494\n",
"10 AAPL 2016-01-19 22.222696 -0.483893\n",
"11 AAPL 2016-01-20 22.252586 0.134502\n",
"12 AAPL 2016-01-21 22.139933 -0.506250\n",
"13 AAPL 2016-01-22 23.317051 5.316720\n",
"14 AAPL 2016-01-25 22.861835 -1.952286"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# download stock prices \n",
"# here, from yahoo: not my fav source, but quick. \n",
"# we need to do some data manipulation to get the data ready \n",
"stock_prices = pdr.get_data_yahoo(stocks, start=start, end=end)\n",
"stock_prices = stock_prices.filter(like='Adj Close') # reduce to just columns with this in the name\n",
"stock_prices.columns = stocks # put their tickers as column names\n",
"\n",
"# refmt from wide to long\n",
"stock_prices = stock_prices.stack().swaplevel().sort_index().reset_index()\n",
"stock_prices.columns = ['Firm','Date','Adj Close']\n",
"\n",
"# add return var = pct_change() function compares to prior row\n",
"# EXCEPT: don't compare for first row of one firm with last row of prior firm!\n",
"# MAKE SURE YOU CREATE THE VARIABLES WITHIN EACH FIRM - use groupby\n",
"stock_prices['ret'] = stock_prices.groupby('Firm')['Adj Close'].pct_change()\n",
"stock_prices['ret'] = stock_prices['ret'] *100 # convert to p.p. to match FF's convention on scaling (daily % rets)\n",
"stock_prices.head(15)"
]
},
{
"cell_type": "markdown",
"id": "fa103d55-2c4d-487b-a706-3037110e42aa",
"metadata": {
"tags": []
},
"source": [
"## Get the factor returns \n",
"\n",
"Above, we got the asset returns, $r_i$ (called \"ret\" in the dataframe). \n",
"\n",
"To estimate $\\alpha$ and $\\beta$ in $r_i-r_f = \\alpha + \\beta (r_m-r_f)$, we need $(r_m-r_f)$ and $r_f$. Let's download those now.\n",
"\n",
"```{margin}\n",
"Fama and French (abbreviated as FF) [have a free data library](https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html). It contains lots of datasets. Values in all of their datasets are\n",
"\n",
"1. percentage returns (return * 100)\n",
"2. for some assets (the columns)\n",
"3. over the period between two dates.\n",
"```\n",
"\n",
"Note: $(r_m-r_f)$ is the excess return on the market, which is _one_ \"factor\". Hedge funds typically use 5 factors in tests. \n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f7ab3d22-5647-4163-93ed-e82b791a50a2",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" Date | \n",
" mkt_excess | \n",
" SMB | \n",
" HML | \n",
" RMW | \n",
" CMA | \n",
" RF | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 2016-01-04 | \n",
" -1.59 | \n",
" -0.76 | \n",
" 0.52 | \n",
" 0.35 | \n",
" 0.40 | \n",
" 0.000 | \n",
"
\n",
" \n",
" 1 | \n",
" 2016-01-05 | \n",
" 0.12 | \n",
" -0.24 | \n",
" 0.01 | \n",
" 0.07 | \n",
" 0.31 | \n",
" 0.000 | \n",
"
\n",
" \n",
" 2 | \n",
" 2016-01-06 | \n",
" -1.35 | \n",
" -0.22 | \n",
" 0.00 | \n",
" 0.16 | \n",
" 0.05 | \n",
" 0.000 | \n",
"
\n",
" \n",
" 3 | \n",
" 2016-01-07 | \n",
" -2.44 | \n",
" -0.28 | \n",
" 0.09 | \n",
" 0.52 | \n",
" 0.36 | \n",
" 0.000 | \n",
"
\n",
" \n",
" 4 | \n",
" 2016-01-08 | \n",
" -1.11 | \n",
" -0.51 | \n",
" -0.04 | \n",
" 0.25 | \n",
" 0.06 | \n",
" 0.000 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 247 | \n",
" 2016-12-23 | \n",
" 0.19 | \n",
" 0.52 | \n",
" -0.50 | \n",
" -0.40 | \n",
" -0.15 | \n",
" 0.001 | \n",
"
\n",
" \n",
" 248 | \n",
" 2016-12-27 | \n",
" 0.27 | \n",
" 0.23 | \n",
" 0.14 | \n",
" 0.14 | \n",
" 0.04 | \n",
" 0.001 | \n",
"
\n",
" \n",
" 249 | \n",
" 2016-12-28 | \n",
" -0.87 | \n",
" -0.29 | \n",
" 0.09 | \n",
" 0.19 | \n",
" -0.15 | \n",
" 0.001 | \n",
"
\n",
" \n",
" 250 | \n",
" 2016-12-29 | \n",
" -0.04 | \n",
" 0.10 | \n",
" -0.33 | \n",
" 0.27 | \n",
" 0.02 | \n",
" 0.001 | \n",
"
\n",
" \n",
" 251 | \n",
" 2016-12-30 | \n",
" -0.52 | \n",
" -0.06 | \n",
" 0.20 | \n",
" -0.09 | \n",
" 0.03 | \n",
" 0.001 | \n",
"
\n",
" \n",
"
\n",
"
252 rows × 7 columns
\n",
"
"
],
"text/plain": [
" Date mkt_excess SMB HML RMW CMA RF\n",
"0 2016-01-04 -1.59 -0.76 0.52 0.35 0.40 0.000\n",
"1 2016-01-05 0.12 -0.24 0.01 0.07 0.31 0.000\n",
"2 2016-01-06 -1.35 -0.22 0.00 0.16 0.05 0.000\n",
"3 2016-01-07 -2.44 -0.28 0.09 0.52 0.36 0.000\n",
"4 2016-01-08 -1.11 -0.51 -0.04 0.25 0.06 0.000\n",
".. ... ... ... ... ... ... ...\n",
"247 2016-12-23 0.19 0.52 -0.50 -0.40 -0.15 0.001\n",
"248 2016-12-27 0.27 0.23 0.14 0.14 0.04 0.001\n",
"249 2016-12-28 -0.87 -0.29 0.09 0.19 -0.15 0.001\n",
"250 2016-12-29 -0.04 0.10 -0.33 0.27 0.02 0.001\n",
"251 2016-12-30 -0.52 -0.06 0.20 -0.09 0.03 0.001\n",
"\n",
"[252 rows x 7 columns]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# We need (r_mkt - rf), and rf\n",
"# the Fama French data library is a benchmark asset pricing dataset \n",
"ff = pdr.get_data_famafrench('F-F_Research_Data_5_Factors_2x3_daily',start=start,end=end)[0] # the [0] is because the imported obect is a dictionary, and key=0 is the dataframe\n",
"ff = ff.reset_index().rename(columns={\"Mkt-RF\":\"mkt_excess\"})\n",
"ff"
]
},
{
"cell_type": "markdown",
"id": "80827786-d69c-4e58-9183-4a58c8656b9e",
"metadata": {
"tags": []
},
"source": [
"## Merge the asset and factor returns "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "2ebb62ff-aae6-4559-b9c4-278c6a318d1a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" Firm | \n",
" Date | \n",
" Adj Close | \n",
" ret | \n",
" mkt_excess | \n",
" SMB | \n",
" HML | \n",
" RMW | \n",
" CMA | \n",
" RF | \n",
" _merge | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" AAPL | \n",
" 2016-01-04 | \n",
" 24.220573 | \n",
" NaN | \n",
" -1.59 | \n",
" -0.76 | \n",
" 0.52 | \n",
" 0.35 | \n",
" 0.40 | \n",
" 0.000 | \n",
" both | \n",
"
\n",
" \n",
" 1 | \n",
" MSFT | \n",
" 2016-01-04 | \n",
" 49.591057 | \n",
" NaN | \n",
" -1.59 | \n",
" -0.76 | \n",
" 0.52 | \n",
" 0.35 | \n",
" 0.40 | \n",
" 0.000 | \n",
" both | \n",
"
\n",
" \n",
" 2 | \n",
" SBUX | \n",
" 2016-01-04 | \n",
" 52.108959 | \n",
" NaN | \n",
" -1.59 | \n",
" -0.76 | \n",
" 0.52 | \n",
" 0.35 | \n",
" 0.40 | \n",
" 0.000 | \n",
" both | \n",
"
\n",
" \n",
" 3 | \n",
" AAPL | \n",
" 2016-01-05 | \n",
" 23.613630 | \n",
" -2.505899 | \n",
" 0.12 | \n",
" -0.24 | \n",
" 0.01 | \n",
" 0.07 | \n",
" 0.31 | \n",
" 0.000 | \n",
" both | \n",
"
\n",
" \n",
" 4 | \n",
" MSFT | \n",
" 2016-01-05 | \n",
" 49.817291 | \n",
" 0.456200 | \n",
" 0.12 | \n",
" -0.24 | \n",
" 0.01 | \n",
" 0.07 | \n",
" 0.31 | \n",
" 0.000 | \n",
" both | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 751 | \n",
" MSFT | \n",
" 2016-12-29 | \n",
" 58.483051 | \n",
" -0.142872 | \n",
" -0.04 | \n",
" 0.10 | \n",
" -0.33 | \n",
" 0.27 | \n",
" 0.02 | \n",
" 0.001 | \n",
" both | \n",
"
\n",
" \n",
" 752 | \n",
" SBUX | \n",
" 2016-12-29 | \n",
" 51.127605 | \n",
" -0.053199 | \n",
" -0.04 | \n",
" 0.10 | \n",
" -0.33 | \n",
" 0.27 | \n",
" 0.02 | \n",
" 0.001 | \n",
" both | \n",
"
\n",
" \n",
" 753 | \n",
" AAPL | \n",
" 2016-12-30 | \n",
" 27.220133 | \n",
" -0.779584 | \n",
" -0.52 | \n",
" -0.06 | \n",
" 0.20 | \n",
" -0.09 | \n",
" 0.03 | \n",
" 0.001 | \n",
" both | \n",
"
\n",
" \n",
" 754 | \n",
" MSFT | \n",
" 2016-12-30 | \n",
" 57.776413 | \n",
" -1.208279 | \n",
" -0.52 | \n",
" -0.06 | \n",
" 0.20 | \n",
" -0.09 | \n",
" 0.03 | \n",
" 0.001 | \n",
" both | \n",
"
\n",
" \n",
" 755 | \n",
" SBUX | \n",
" 2016-12-30 | \n",
" 50.401356 | \n",
" -1.420465 | \n",
" -0.52 | \n",
" -0.06 | \n",
" 0.20 | \n",
" -0.09 | \n",
" 0.03 | \n",
" 0.001 | \n",
" both | \n",
"
\n",
" \n",
"
\n",
"
756 rows × 11 columns
\n",
"
"
],
"text/plain": [
" Firm Date Adj Close ret mkt_excess SMB HML RMW CMA \\\n",
"0 AAPL 2016-01-04 24.220573 NaN -1.59 -0.76 0.52 0.35 0.40 \n",
"1 MSFT 2016-01-04 49.591057 NaN -1.59 -0.76 0.52 0.35 0.40 \n",
"2 SBUX 2016-01-04 52.108959 NaN -1.59 -0.76 0.52 0.35 0.40 \n",
"3 AAPL 2016-01-05 23.613630 -2.505899 0.12 -0.24 0.01 0.07 0.31 \n",
"4 MSFT 2016-01-05 49.817291 0.456200 0.12 -0.24 0.01 0.07 0.31 \n",
".. ... ... ... ... ... ... ... ... ... \n",
"751 MSFT 2016-12-29 58.483051 -0.142872 -0.04 0.10 -0.33 0.27 0.02 \n",
"752 SBUX 2016-12-29 51.127605 -0.053199 -0.04 0.10 -0.33 0.27 0.02 \n",
"753 AAPL 2016-12-30 27.220133 -0.779584 -0.52 -0.06 0.20 -0.09 0.03 \n",
"754 MSFT 2016-12-30 57.776413 -1.208279 -0.52 -0.06 0.20 -0.09 0.03 \n",
"755 SBUX 2016-12-30 50.401356 -1.420465 -0.52 -0.06 0.20 -0.09 0.03 \n",
"\n",
" RF _merge \n",
"0 0.000 both \n",
"1 0.000 both \n",
"2 0.000 both \n",
"3 0.000 both \n",
"4 0.000 both \n",
".. ... ... \n",
"751 0.001 both \n",
"752 0.001 both \n",
"753 0.001 both \n",
"754 0.001 both \n",
"755 0.001 both \n",
"\n",
"[756 rows x 11 columns]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"assets_and_factors = pd.merge(\n",
" left=stock_prices,\n",
" right=ff,\n",
" on=\"Date\",\n",
" how=\"inner\",\n",
" indicator=True,\n",
" validate=\"many_to_one\",\n",
")\n",
"assets_and_factors"
]
},
{
"cell_type": "markdown",
"id": "fb4aec9c-205d-48c2-9dc0-b26a2d15f046",
"metadata": {
"tags": []
},
"source": [
"## Estimate CAPM\n",
"\n",
"So the data’s basically ready. _(We need to do two quick things below.)_\n",
"\n",
"Again, the goal is to estimate, **for each** firm, **for each** year, alpha and beta, from the CAPM formula.\n",
"\n",
"Well, as we've said, if you are doing a \"for each\" on a dataframe, that means you want to use groupby!\n",
"\n",
"So, I have a dataframe, and **for each** firm, and **for each** year, I want to \\ (run regressions).\n",
"\n",
"That almost directly translates to the code we need: `assets_and_factors.groupby([firm,year]).runregression()`. Except there is no \"runregression\" function that applies to pandas groupby objects. But we can write such a function and then `apply()` it. Meaning, our plan is to basically use this code: `assets_and_factors.groupby([firm,year]).apply()`.\n",
"\n",
"We just need to write a reg function that works on groupby objects."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d2d83ab4-f7d5-4914-b59e-9c4e844e51ed",
"metadata": {},
"outputs": [],
"source": [
"import statsmodels.api as sm\n",
"\n",
"\n",
"def reg_in_groupby(df, formula=\"ret_excess ~ mkt_excess + SMB + HML\"):\n",
" \"\"\"\n",
" Want to run regressions after groupby? E.g., repeat the regression \n",
" for each firm-year?\n",
" \n",
" This will do it! \n",
" \n",
" Note: This defaults to a FF3 model assuming specific variable names. If you\n",
" want to run any other regression, just specify your model.\n",
" \n",
" Usage: \n",
" df.groupby().apply(reg_in_groupby)\n",
" df.groupby().apply(reg_in_groupby,formula=)\n",
" \"\"\"\n",
" return pd.Series(sm.formula.ols(formula, data=df).fit().params)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "6bc697ad-e79c-42b7-b283-20281abc9dc4",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" Firm | \n",
" year | \n",
" alpha | \n",
" beta | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" AAPL | \n",
" 2016 | \n",
" -0.001081 | \n",
" 0.965622 | \n",
"
\n",
" \n",
" 1 | \n",
" MSFT | \n",
" 2016 | \n",
" 0.000690 | \n",
" 1.166076 | \n",
"
\n",
" \n",
" 2 | \n",
" SBUX | \n",
" 2016 | \n",
" -0.056862 | \n",
" 0.848162 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Firm year alpha beta\n",
"0 AAPL 2016 -0.001081 0.965622\n",
"1 MSFT 2016 0.000690 1.166076\n",
"2 SBUX 2016 -0.056862 0.848162"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(\n",
" assets_and_factors # grab the data\n",
" \n",
" # Two things before the regressions:\n",
" # 1. need a year variable (to group on)\n",
" # 2. the market returns in FF are excess returns, so \n",
" # our stock returns need to be excess as well\n",
" .assign(year = assets_and_factors.Date.dt.year,\n",
" ret_excess = assets_and_factors.ret - assets_and_factors.RF)\n",
" \n",
" # ok, run the regs, so easy!\n",
" .groupby(['Firm','year']).apply(reg_in_groupby,formula='ret_excess ~ mkt_excess')\n",
" \n",
" # and clean up - with better var names\n",
" .rename(columns={'Intercept':'alpha','mkt_excess':'beta'})\n",
" .reset_index()\n",
")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}