Files
strategy-lab/to_explore/pyquantnews/24_FactorAnalysis.ipynb
David Brazda e3da60c647 daily update
2024-10-21 20:57:56 +02:00

6.9 KiB

No description has been provided for this image

This code performs a multifactor analysis on monthly stock returns, applying the Fama-French three-factor model for financial analysis. It fetches historical factor data, calculates active returns of selected stocks, and estimates their sensitivities to the Fama-French factors. The code also performs rolling regression to analyze the stability of factor exposures over time. Lastly, it calculates and prints the marginal contributions to risk from each factor.

In [ ]:
import numpy as np
import pandas as pd
In [ ]:
import pandas_datareader as pdr
import yfinance as yf
In [ ]:
import statsmodels.api as sm
from statsmodels import regression
from statsmodels.regression.rolling import RollingOLS

Fetch Fama-French factors data starting from 2000-01-01 and select the SMB and HML factors

In [ ]:
factors = pdr.get_data_famafrench(
    'F-F_Research_Data_Factors',
    start='2000-01-01'
)[0][1:]
In [ ]:
SMB = factors.SMB
HML = factors.HML

Download monthly adjusted close prices for specified stocks starting from 2000-01-01

In [ ]:
data = yf.download(
    ['SPY', 'MSFT', 'AAPL', 'INTC'], 
    start="2000-01-01", 
    interval="1mo"
)['Adj Close']

Calculate the monthly returns and convert them to period-based returns

In [ ]:
monthly_returns = data.pct_change().to_period("M")

Extract the benchmark returns (SPY) and calculate active returns against the benchmark

In [ ]:
bench = monthly_returns.pop("SPY")
R = monthly_returns.mean(axis=1)
active = R - bench

Create a DataFrame with active returns and Fama-French factors SMB and HML

In [ ]:
df = pd.DataFrame({
    'R': active,
    'F1': SMB,
    'F2': HML,
}).dropna()

Perform Ordinary Least Squares (OLS) regression to estimate sensitivities to the factors

In [ ]:
b1, b2 = regression.linear_model.OLS(
    df.R, 
    df[['F1', 'F2']]
).fit().params
In [ ]:
print(f'Sensitivities of active returns to factors:\nSMB: {b1}\nHML: {b2}')

Perform rolling OLS regression to estimate how factor sensitivities change over time

In [ ]:
exog_vars = ["SMB", "HML"]
exog = sm.add_constant(factors[exog_vars])
rols = RollingOLS(df.R, exog, window=12)
rres = rols.fit()
fig = rres.plot_recursive_coefficient(variables=exog_vars)

Calculate covariance between factors and marginal contributions to active risk (MCAR) for each factor

In [ ]:
F1 = df.F1
F2 = df.F2
In [ ]:
cov = np.cov(F1, F2)
ar_squared = (active.std())**2
mcar1 = (b1 * (b2 * cov[0,1] + b1 * cov[0,0])) / ar_squared
mcar2 = (b2 * (b1 * cov[0,1] + b2 * cov[1,1])) / ar_squared
print(f'SMB risk contribution: {mcar1}')
print(f'HML risk contribution: {mcar2}')
print(f'Unexplained risk contribution: {1 - (mcar1 + mcar2)}')

PyQuant News is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to get started with Python for quant finance. For educational purposes. Not investment advise. Use at your own risk.