手把手教你用Python复现Fama-French五因子模型(附完整代码)
用Python实战Fama-French五因子模型:从数据获取到策略回测全流程
在量化投资领域,Fama-French五因子模型一直是资产定价研究的重要里程碑。不同于教科书式的理论讲解,本文将带您用Python完整实现该模型——从原始数据清洗到因子构建,再到最终的组合回测。无论您是刚接触量化的学生,还是希望巩固基础的从业者,这套可直接复用的代码框架都能为您节省大量摸索时间。
1. 环境准备与数据源选择
工欲善其事,必先利其器。我们首先需要搭建一个稳定的研究环境。推荐使用Anaconda创建独立Python环境:
conda create -n ff5 python=3.9 conda activate ff5 pip install pandas numpy statsmodels yfinance matplotlib数据获取是模型构建的第一道门槛。对于A股市场,我们可以通过以下渠道获取必要数据:
| 数据类型 | 推荐来源 | 更新频率 | 关键字段 |
|---|---|---|---|
| 股票价格 | Tushare Pro / AKShare | 日频 | 收盘价、复权因子 |
| 财务数据 | Wind API / CSMAR数据库 | 季度 | 账面价值、净利润 |
| 无风险利率 | 央行官网 / 同业拆借市场 | 日频 | SHIBOR利率 |
| 市值信息 | 交易所公开数据 | 日频 | 流通市值、总市值 |
提示:使用Tushare Pro需注册获取token,免费版即可满足基础需求。若处理美股数据,可直接调用yfinance库获取雅虎财经数据。
2. 因子构建的核心逻辑
五因子模型在传统三因子(市场、规模、价值)基础上,新增了盈利因子(RMW)和投资因子(CMA)。这些因子的计算需要严格遵循Fama-French的原始论文方法:
2.1 规模因子(SMB)与价值因子(HML)
构建过程分为三个关键步骤:
- 市值分组:每年6月末按总市值中位数将股票分为大(B)小(S)两组
- 账面市值比分组:按30%/70%分位数分为高(H)、中(N)、低(L)三组
- 组合收益计算:
- SMB = (SH + SN + SL)/3 - (BH + BN + BL)/3
- HML = (SH + BH)/2 - (SL + BL)/2
def calculate_smb_hml(df): # 计算市值中位数 median_size = df['market_cap'].median() # 计算BM 30%和70%分位数 bm_30, bm_70 = df['bm'].quantile(0.3), df['bm'].quantile(0.7) # 分组标记 df['size_group'] = np.where(df['market_cap'] > median_size, 'B', 'S') df['bm_group'] = np.select( [df['bm'] <= bm_30, df['bm'] >= bm_70], ['L', 'H'], default='N' ) # 计算组合收益率(需连接实际收益率数据) ...2.2 盈利因子(RMW)与投资因子(CMA)
这两个因子的构建更为复杂,需要处理财务数据的时滞效应:
- RMW因子:使用t-1年12月的盈利能力数据,计算t年7月至t+1年6月的组合收益差
- CMA因子:基于t-1财年的投资水平数据,同样计算后续12个月组合收益差
def calculate_rmw_cma(financial_df, return_df): # 财务数据与收益率数据对齐 merged = pd.merge_asof( return_df.sort_index(), financial_df.sort_index(), left_index=True, right_index=True, direction='backward' ) # 计算盈利能力(OP)和投资水平(Inv) merged['OP'] = merged['ebit'] / merged['equity'] merged['Inv'] = merged['asset_growth'] # 按分位数分组 ...3. 回归分析与因子检验
获得各因子时间序列后,我们需要检验其解释能力。使用statsmodels进行时间序列回归:
import statsmodels.api as sm def factor_regression(stock_ret, factors): """ 五因子模型回归分析 """ X = sm.add_constant(factors[['MKT', 'SMB', 'HML', 'RMW', 'CMA']]) model = sm.OLS(stock_ret - factors['RF'], X) results = model.fit() print(results.summary()) return results.params, results.tvalues关键输出指标解读:
| 指标 | 理想范围 | 经济含义 |
|---|---|---|
| R-squared | 0.6-0.8 | 模型解释力 |
| MKT beta | 显著为正 | 系统风险暴露 |
| SMB系数 | 因策略而异 | 小盘股溢价 |
| Alpha | 统计不显著 | 超额收益(理想值为0) |
注意:回归前需检查因子间的多重共线性,可通过VIF检验。若VIF>5,需考虑因子正交化处理。
4. 策略实现与回测框架
基于五因子模型可以开发多种策略,这里展示一个简单的多空组合策略:
- 股票筛选:每月末对所有股票进行五因子回归
- 组合构建:
- 做多alpha最高(被低估)的前10%股票
- 做空alpha最低(被高估)的后10%股票
- 权重分配:等权配置,每月再平衡
def factor_strategy_backtest(data): portfolio = {} for date, month_data in data.groupby(pd.Grouper(freq='M')): # 计算各股票因子暴露 factor_exposures = calculate_exposures(month_data) # 预测收益率 expected_return = factor_exposures.dot(factor_premiums) # 构建多空组合 long_stocks = expected_return.nlargest(int(len(expected_return)*0.1)) short_stocks = expected_return.nsmallest(int(len(expected_return)*0.1)) # 记录组合 portfolio[date] = { 'long': long_stocks.index.tolist(), 'short': short_stocks.index.tolist() } # 计算策略收益(需连接价格数据) ...回测结果评估应包含以下核心指标:
def evaluate_performance(returns): metrics = { '年化收益': annualized_return(returns), '波动率': annualized_volatility(returns), '夏普比率': sharpe_ratio(returns), '最大回撤': max_drawdown(returns), '胜率': win_rate(returns) } # 因子归因分析 factor_attribution = factor_regression(returns, factors) return pd.Series(metrics), factor_attribution5. 常见问题与优化方向
实际应用中会遇到各种技术挑战,以下是几个典型问题的解决方案:
数据对齐问题:
- 财务数据公布存在滞后,需确保使用"已知"数据
- 处理停牌股票时,建议使用前复权价格
# 财务数据对齐示例 financial_data['report_date'] = pd.to_datetime(financial_data['report_date']) financial_data['effective_date'] = financial_data['report_date'] + pd.DateOffset(months=3)参数优化空间:
- 因子计算周期:尝试月度/季度再平衡
- 分组标准:调整市值和BM的分组分位数
- 权重方案:测试等权 vs 市值加权 vs 风险平价
进阶扩展建议:
- 加入动量因子构成六因子模型
- 尝试机器学习方法估计因子载荷
- 开发基于因子Z-score的动态配置策略
在实盘部署时,务必注意交易成本的影响。我们的测试显示,当单边交易成本超过0.2%时,传统五因子策略的超额收益可能被完全侵蚀。这时需要考虑:
- 降低换手率的过滤规则
- 引入交易成本模型优化调仓
- 使用衍生品对冲部分风险
