# 手把手教你实现法玛三因子模型

## 关于法玛三因子模型

E(Ri) = Rf + βi(Market Risk Premium) + si(SMB) + hi(HML)

## Python代码实现

import pandas as pd
import tushare as ts

# 获取股票列表
stock_list = ts.get_stock_basics().index.tolist()

# 获取股票市值数据
market_cap = ts.get_stock_basics().loc[:, 'totalAssets']

# 获取股票账面市值比数据
book_to_market = ts.get_stock_basics().loc[:, 'bvps'] / ts.get_stock_basics().loc[:, 'pb']

# 将市值和账面市值比数据合并到一个DataFrame中
data = pd.concat([market_cap, book_to_market], axis=1)
data.columns = ['market_cap', 'book_to_market']
data.index.name = 'code'
data = data.dropna()


import numpy as np
import statsmodels.api as sm

# 获取股票收益率数据
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
try:
stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq',
return_data[code] = stock_return
except:
pass

# 计算市场指数收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date,
market_return.name = 'market_return'

# 将股票收益率和市场指数收益率合并到一个DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 计算超额收益率
data = data.sub(data['market_return'], axis=0)

# 将市值和账面市值比数据合并到一个DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 计算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date,

# 计算法玛三因子模型的参数
model = sm.OLS(np.array(data.mean()), X)
results = model.fit()
print(results.summary())


## 因子有效性检验

1 因子收益率的t检验

import pandas as pd
import numpy as np
import statsmodels.api as sm

# 获取股票收益率数据
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
try:
stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq',
return_data[code] = stock_return
except:
pass

# 计算市场指数收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date,
market_return.name = 'market_return'

# 将股票收益率和市场指数收益率合并到一个DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 计算超额收益率
data = data.sub(data['market_return'], axis=0)

# 将市值和账面市值比数据合并到一个DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 计算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date,

# 计算t检验的p值
ttest = sm.stats.ttest_ind(factor_data, np.zeros(factor_data.shape), axis=0)

# 打印检验结果
print(ttest)


2 因子回归的 $R^2$ 值

import pandas as pd
import numpy as np
import statsmodels.api as sm

# 获取股票收益率数据
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
try:
stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq',
return_data[code] = stock_return
except:
pass

# 计算市场指数收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date,
market_return.name = 'market_return'

# 将股票收益率和市场指数收益率合并到一个DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 计算超额收益率
data = data.sub(data['market_return'], axis=0)

# 将市值和账面市值比数据合并到一个DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 计算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date,

# 计算因子回归的R2值
y = data.mean()
model = sm.OLS(y, X).fit()
rsquared = model.rsquared

# 打印检验结果
print(rsquared)

3 因子相关性分析

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 获取股票收益率数据
start_date = '2020-01-01'
end_date = '2020-12-31'
return_data = pd.DataFrame()
for code in stock_list:
try:
stock_return = ts.pro_bar(ts_code=code, start_date=start_date, end_date=end_date, adj='qfq',
return_data[code] = stock_return
except:
pass

# 计算市场指数收益率
market_return = ts.pro_bar(ts_code='000001.SH', start_date=start_date, end_date=end_date,
market_return.name = 'market_return'

# 将股票收益率和市场指数收益率合并到一个DataFrame中
data = pd.concat([return_data, market_return], axis=1)
data = data.dropna()

# 计算超额收益率
data = data.sub(data['market_return'], axis=0)

# 将市值和账面市值比数据合并到一个DataFrame中
data = pd.concat([data, market_cap, book_to_market], axis=1)
data.columns = stock_list + ['market_return', 'market_cap', 'book_to_market']

# 计算因子收益率
factor_data = pd.DataFrame()
factor_data['market_factor'] = market_return - ts.pro_bar(ts_code='000016.SH', start_date=start_date,

# 计算因子相关性
corr_matrix = factor_data.corr()

# 绘制热图
sns.heatmap(corr_matrix, annot=True, cmap='RdYlBu')
plt.show()


## 法玛三因子模型的优缺点

1. 解释力强：相较于CAPM模型，法玛三因子模型的解释力更强，能够更好地解释股票收益率的变化。

2. 考虑了多个因素：相较于单因子模型，法玛三因子模型考虑了市场因子、规模因子和价值因子，更全面地考虑了股票收益率的影响因素。

3. 可解释性强：法玛三因子模型中的三个因子，即市场因子、规模因子和价值因子，都是经济学上有实际意义的因素，因此其结果更容易被解释。

1. 忽略了其他因素：法玛三因子模型只考虑了市场因子、规模因子和价值因子，忽略了其他可能对股票收益率有影响的因素，如流动性、动量等。

2. 样本限制：法玛三因子模型的样本通常是历史股票数据，而历史表现并不能保证未来表现，因此其预测能力有限。

3. 可能存在共线性问题：法玛三因子模型中的因子可能存在共线性问题，导致其解释能力下降。

4. 不适用于所有市场：法玛三因子模型的适用范围有限，可能无法适用于所有市场。例如，一些新兴市场可能存在不同的因子影响股票收益率，无法使用法玛三因子模型来解释其表现。

## 如何改进提升

1 添加其他因子：法玛三因子模型只考虑了市场因子、规模因子和价值因子，可以添加其他因子，如动量、流动性等，来提升模型的解释能力。可以使用pyfolio库中的get_factor_returns函数获取更多的因子数据，例如动量因子和波动率因子：

import pyfolio as pf

start_date = '2015-01-01'
end_date = '2021-12-31'
tickers = ['AAPL', 'MSFT', 'AMZN', 'GOOG', 'FB']
factor_names = ['market_beta', 'size_factor', 'value_factor', 'momentum_factor', 'volatility_factor']

factor_data = pf.utils.get_factor_returns(factor_names, start_date=start_date, end_date=end_date)


2 考虑时间变化：股票市场中因子的影响可能会随着时间变化而变化，可以建立时间变化的因子模型，或者采用滚动回归来考虑时间变化对因子的影响。 可以使用rolling函数进行滚动回归，并将时间窗口设置为1年或更长时间：

import pandas as pd
import statsmodels.api as sm

rolling_window = 252
factor_data_rolling = pd.DataFrame(index=factor_data.index)
for factor_name in factor_names:
factor_data_rolling[factor_name] = factor_data[factor_name].rolling(window=rolling_window).apply(lambda x: sm.OLS(x, sm.add_constant(factor_data[['market_beta', 'size_factor', 'value_factor']]).loc[x.index]).fit().params)

factor_data_rolling = factor_data_rolling.dropna()


3 考虑非线性关系：股票收益率和因子之间可能存在非线性关系，可以使用非线性回归模型来建立因子模型，或者使用机器学习方法来建立预测模型。 可以使用scikit-learn库中的多项式回归模型来建立非线性关系的因子模型：

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

poly_degree = 2
poly_features = PolynomialFeatures(poly_degree, include_bias=False)
X_poly = poly_features.fit_transform(factor_data[['market_beta', 'size_factor', 'value_factor']])
model = LinearRegression().fit(X_poly, factor_data['returns'])


或者使用scikit-learn库中的机器学习模型来建立因子模型，例如随机森林模型：

from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(factor_data[['market_beta', 'size_factor', 'value_factor']], factor_data['returns'])


4 解决共线性问题：法玛三因子模型中的因子可能存在共线性问题，可以使用主成分分析等方法来减少因子之间的共线性，提高模型的解释能力。 可以使用scikit-learn库中的主成分分析模型来减少因子之间的共线性：

from sklearn.decomposition import PCA

n_components = 3
pca = PCA(n_components=n_components)
X_pca = pca.fit_transform(factor_data[['market_beta', 'size_factor', 'value_factor']])


5 使用更多数据：使用更多的数据来建立因子模型，可以提高模型的预测能力和稳健性。 可以使用Quandl等数据源获取更多的历史数据来建立因子模型：

import quandl

quandl.ApiConfig.api_key = 'your_api_key'
data = quandl.get_table('SHARADAR/SF1', ticker=tickers, dimension='MRY', qopts={'columns': ['ticker', 'date', 'marketcap', 'roe', 'pb']})
data = data.pivot(index='date', columns='ticker')
data.columns = [f"{col[0]}_{col[1]}" for col in data.columns]
data = data.dropna()


6 考虑国别和行业因素：股票收益率受到不同国别和行业因素的影响，可以建立考虑国别和行业因素的多因子模型，来提高模型的解释能力。 可以使用pyfolio库中的get_industry_returns函数获取行业因子数据，并使用alpha_vantage等数据源获取国别因子数据：

import alpha_vantage
from alpha_vantage.timeseries import TimeSeries
import pyfolio as pf

# 使用alpha_vantage获取国别因子数据
ts = TimeSeries(key='YOUR_API_KEY', output_format='pandas')
data, meta_data = ts.get_daily(symbol='SPY', outputsize='full')
data.columns = ['open', 'high', 'low', 'close', 'volume']
data = data[['close']]
data = data.pct_change().dropna()
data.columns = ['market_factor']

# 使用pyfolio获取行业因子数据
industry_data = pf.utils.get_industry_returns('morningstar', 'usa')
industry_data.columns = ['industry_factor']

# 合并国别和行业因子数据
factor_data = pd.concat([data, industry_data], axis=1).dropna()
factor_data = factor_data.resample('M').last()

# 运用法玛三因子模型进行分析
...


7 考虑投资组合构建方法：可以使用优化模型来构建投资组合，例如使用CVXPY库中的优化模型：

pythonCopy codeimport cvxpy as cp

weights = cp.Variable(3)
constraints = [cp.sum(weights) == 1, weights >= 0]
expected_return = factor_data_rolling['returns'].mean()
cov_matrix = factor_data_rolling[['market_beta', 'size_factor', 'value_factor']].cov()