用Python复刻Fama-French因子模型:量化你的投资策略到底强在哪

我第一次接触因子投资,是在一篇学术论文的脚注里。1992年,Eugene Fama和Kenneth French提出了三因子模型,核心观点是:股票的回报率由三个因素解释——市场暴露(Beta)、公司规模(小盘 vs 大盘)和估值(便宜 vs 贵)。几年后他们又加入了盈利能力和投资因子,变成了五因子。这个模型的厉害之处不在于“发现了这些因子”——投资者早就知道小盘股和价值股有超额收益——而在于:这些因子是系统性、可测量、能用公开数据在回归模型里复现的。

作为开发者,我瞬间被吸引了:如果回报率能被分解成因子暴露,我就能量化自己的组合为什么赚钱。我跑赢市场是因为选股水平高,还是因为不小心在小盘价值行情里重仓了小盘价值股?Fama-French模型用系数和p-value回答这个问题。以下就是不用Bloomberg终端、用Python实现的完整步骤。

数据:免费获取(而且超简单)

Fama-French因子回报数据由达特茅斯学院的Ken French数据图书馆免费发布,不需要API key,也不需要数据订阅。原始数据以CSV文件形式提供,每月更新:

https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip

解压后,CSV包含五列:Mkt-RF(市场回报减去无风险利率)、SMB(小盘减大盘)、HML(高账面市值比减低账面市值比)、RF(无风险利率)。这是三因子。如果要五因子,下载F-F_Research_Data_5_Factors_2x3_CSV.zip,会多出RMW(高盈利能力减低盈利能力)和CMA(保守投资减激进投资)。

把数据加载到Python需要做一些小清理——CSV开头有一段标题说明,末尾有版权声明:

import pandas as pd

# 三因子数据下载地址
url = ("https://mba.tuck.dartmouth.edu/pages/faculty/"
       "ken.french/ftp/F-F_Research_Data_Factors_CSV.zip")
# 跳过前3行标题,以第一列为索引(日期),自动解析日期
ff3 = pd.read_csv(url, skiprows=3, index_col=0, parse_dates=True)
# 将索引转换为datetime格式(原格式是YYYYMM)
ff3.index = pd.to_datetime(ff3.index, format='%Y%m')
# 去掉最后一行版权信息
ff3 = ff3.iloc[:-1]
# 把百分比换算成小数(原始数据0.56表示0.56%,直接回归会出问题)
ff3 = ff3 / 100
print(ff3.tail())

除以100这一步非常关键。原始数据把回报率存成百分比(比如0.56代表0.56%),如果你的股票回报率已经是小数,混进去回归得到系数会错得离谱。建模前一定要统一成小数。

提示

如果要拿个股回报率做因变量,用yfinance从雅虎财经下载就行。如果是做组合分析,就用自己的持仓权重计算加权回报。注意Fama-French因子本身是多空组合的回报,所以你的因变量也应该是超额回报(股票回报减去无风险利率),这样才和模型形式匹配。

跑三因子回归

统计核心是一个普通最小二乘回归,用statsmodels搞定。如果你以前写过线性模型,等式长这样:

R_it - R_ft = α_i + β_i(R_mt - R_ft) + s_i(SMB_t) + h_i(HML_t) + ε_it

用Python实现:

import statsmodels.api as sm
import yfinance as yf

# 1. 下载股票数据(以苹果为例)
stock = yf.download('AAPL', start='2018-01-01', progress=False)
# 计算月度回报(月末复利)
stock_returns = stock['Adj Close'].pct_change().resample('M').agg(
    lambda x: (1 + x).prod() - 1
)

# 2. 对齐数据:把因子数据也按月换算(复利)
stock_returns.index = stock_returns.index.to_period('M')
ff3_monthly = ff3.resample('M').agg(lambda x: (1 + x).prod() - 1)
ff3_monthly.index = ff3_monthly.index.to_period('M')

# 3. 合并并计算超额回报
data = pd.concat([stock_returns, ff3_monthly], axis=1).dropna()
data.columns = ['stock_ret', 'mkt_rf', 'smb', 'hml', 'rf']
data['excess_ret'] = data['stock_ret'] - data['rf']

# 4. 执行回归
X = sm.add_constant(data[['mkt_rf', 'smb', 'hml']])
y = data['excess_ret']
model = sm.OLS(y, X).fit()
print(model.summary())

输出里你最关心的是α(截距项,即alpha)。如果alpha为正且统计显著,说明这只股票在因子暴露之外产生了超额收益;如果是负的,说明它跑输了。mkt_rf系数就是市场Beta——如果是1.2,意味着该股票平均比市场多涨20%。smbhml系数告诉你股票的规模和价值倾向。

大多数个股的R-squared在0.2~0.4之间,这很正常——因子模型是为分散化组合设计的,单个股票的alpha基本是噪音,因子载荷才是有用信息。

扩展到五因子模型

增加盈利和投资因子只是机械地多加两列:

# 下载五因子数据
ff5 = pd.read_csv(
    "https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/"
    "ftp/F-F_Research_Data_5_Factors_2x3_CSV.zip",
    skiprows=3, index_col=0, parse_dates=True
)
ff5.index = pd.to_datetime(ff5.index, format='%Y%m')
ff5 = ff5.iloc[:-1] / 100   # 转换成小数

# 用同样的超额回报y,但X增加两列
X5 = sm.add_constant(data[['mkt_rf', 'smb', 'hml', 'rmw', 'cma']])
model5 = sm.OLS(y, X5).fit()

实际问题是:五因子比三因子到底提升了多少?对美股大盘股来说,通常提升很小——加RMW和CMA后R-squared增长微乎其微。但对小盘股和海外市场,五因子能解释更多方差。正确的做法是两种都跑,对比调整后的R-squared和AIC。

因子载荷告诉你什么

这些系数不是抽象的统计量,它们用直白的语言描述你的组合在不同市场环境下的表现:

  • 高市场Beta(>1.1): 牛市里你的组合会跑赢,但回调时会跌得更惨。这是大多数组合回报的主要来源。如果你在市场Beta加入模型后alpha消失了,说明你根本没有产生alpha——你只是在用杠杆。
  • 正的SMB载荷: 你持有小盘股。历史数据表明小盘长期跑赢大盘,但在衰退时跌幅也更深。如果组合SMB > 0.5,说明不管你本意如何,你都在做小盘赌注。
  • 正的HML载荷: 价值倾向。你持有低市净率的股票。这个因子从2007年左右开始长期跑输——长达15年的低迷让一些人宣布价值投资已死。但它也曾经是除市场Beta之外长期溢价最高的因子。价值溢价到底还存不存在,这是个哲学问题——HML载荷告诉你你的组合暴露了多少。

警告

因子载荷是后视镜。组合过去5年的SMB载荷并不预测未来的小盘暴露——它只描述已经发生的事。如果你半年前改了策略,用五年数据回归会掩盖这个切换。建议用滚动36个月窗口重新跑,来发现因子暴露随时间的变化。

因子动物园问题

学术文献一开始没告诉你的是:现在金融文献里已经发布超过400种因子。研究者们几乎为一切事物都找到了“溢价”——从股票发行到品牌知名度,再到空气质量。其中大多数是统计假象——数据挖掘出来的模式,一换样本就消失。这就是“因子动物园”的问题。

对于开发投资流程的工程人员来说,实际做法是:只保留那些既有统计溢价、又有行为或风险解释的因子。市场Beta(风险)、规模(流动性风险)、价值(行为过度反应)、盈利(质量持续性)、动量(对新闻反应不足)都经得起复制检验。2016年工作论文里那个“12个月行业调整后的总盈利能力因子”,多半不行。

先从Fama-French三因子开始分析你的组合。如果alpha接近零,R-squared超过0.8,那你的回报已经被市场、规模、价值这三个因子充分解释了。你不是选股天才——你是一个因子配置者。这完全是一个值得骄傲的身份,而且比在个股选择上维持优势轻松得多。


直达网址:https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip

类似文章