Python量化交易——手把手构建一个年化收益超40%的Fama-French三因子选股策略
·
1. Fama-French三因子模型:从理论到实战
1992年,尤金·法玛和肯尼斯·弗兰奇提出了著名的三因子模型,这个模型在传统CAPM模型基础上增加了两个关键因子:市值因子(SMB)和账面市值比因子(HML)。这个模型的核心思想是:股票的超额收益不仅受市场风险影响,还与公司规模和价值特征密切相关。
在实际操作中,我发现这个模型特别适合A股市场。A股市场散户占比高,容易出现小市值效应和价值溢价现象。通过历史数据回测,小市值股票在A股市场长期表现优于大市值股票,而低市净率(高账面市值比)股票也往往能带来超额收益。
模型的核心三因子 :
- 市场风险溢价(Rm-Rf) :这个因子大家最熟悉,代表市场组合收益率减去无风险利率。在A股实操中,我通常用沪深300指数收益率作为市场组合,十年期国债收益率作为无风险利率。
- 市值因子(SMB) :按市值将股票分为大、小两组,用小市值组合收益率减去大市值组合收益率。在A股,这个因子特别有效,小市值效应非常明显。
- 账面市值比因子(HML) :按账面市值比(PB的倒数)将股票分为高、中、低三组,用高组收益率减去低组收益率。这个因子在A股市场波动较大,但长期来看仍然有效。
2. 环境准备与数据获取
2.1 Python环境配置
我推荐使用Anaconda创建独立的Python环境,避免包冲突。以下是具体步骤:
conda create -n quant python=3.8
conda activate quant
pip install qteasy numpy pandas tushare
关键库说明 :
- qteasy :这是我们策略开发的核心工具,它封装了很多量化交易常用功能
- tushare :免费获取A股市场数据,需要注册获取token
- pandas :数据处理必备,建议安装1.3.0以上版本
2.2 数据获取与处理
获取数据是量化交易的第一步,也是最容易踩坑的环节。我总结了几点经验:
- 股票池选择 :建议使用沪深300成分股,流动性好,数据质量高
- 数据清洗 :特别注意处理停牌股票和异常值
- 因子计算 :账面市值比=1/PB,市值直接使用总市值
import qteasy as qt
import tushare as ts
# 初始化tushare
ts.set_token('你的token')
pro = ts.pro_api()
# 获取沪深300成分股
hs300 = pro.index_weight(index_code='000300.SH', start_date='20200101')
stock_pool = hs300['con_code'].tolist()
# 获取股票基本面数据
stock_basic = pro.stock_basic(list_status='L')
pb_data = pro.daily_basic(ts_code=','.join(stock_pool), fields='ts_code,trade_date,pb')
3. 策略实现详解
3.1 自定义策略类开发
qteasy提供了三种策略基类,我们这里使用FactorSorter,它专门为多因子选股设计。开发策略主要需要实现两个方法: init ()和realize()。
class FF3FactorStrategy(qt.FactorSorter):
def __init__(self, pars=(0.3, 0.3, 0.4)):
super().__init__(
pars=pars,
par_count=3,
par_types=['float', 'float', 'float'],
name='FF3Factor',
description='Fama-French三因子选股策略',
strategy_run_freq='m', # 每月调仓
strategy_data_types='pb, total_mv, close',
reference_data_types='close-000300.SH', # 沪深300作为基准
max_sel_count=10, # 最多选10只股票
sort_ascending=True # 选择alpha最小的股票
)
3.2 核心逻辑实现
realize()方法是策略的核心,这里我们需要计算三个因子,然后通过回归得到每只股票的alpha值。
def realize(self, h, r=None, t=None, pars=None):
# 解析参数
size_perc, bp_low_perc, bp_high_perc = pars
# 获取最新数据
pb = h[:, -1, 0] # 市净率
mv = h[:, -1, 1] # 总市值
close = h[:, -1, 2] # 收盘价
pre_close = h[:, -2, 2] # 前收盘价
# 计算市场收益率(沪深300)
market_pre_close = r[-2, 0]
market_close = r[-1, 0]
market_return = market_pre_close / market_close - 1
# 计算账面市值比和股票收益率
bp = 1 / pb # 账面市值比
stock_return = close / pre_close - 1
# 计算SMB因子
size_threshold = np.nanquantile(mv, size_perc)
smb_small = np.nanmean(stock_return[mv <= size_threshold])
smb_big = np.nanmean(stock_return[mv > size_threshold])
smb = smb_small - smb_big
# 计算HML因子
bp_low = np.nanquantile(bp, bp_low_perc)
bp_high = np.nanquantile(bp, bp_high_perc)
hml_high = np.nanmean(stock_return[bp >= bp_high])
hml_low = np.nanmean(stock_return[bp <= bp_low])
hml = hml_high - hml_low
# 计算每只股票的alpha
alphas = []
for ret in stock_return:
X = np.array([[market_return, smb, hml, 1.0]])
y = np.array([[ret]])
alpha = np.linalg.lstsq(X, y, rcond=None)[0][3][0]
alphas.append(alpha)
return np.array(alphas)
4. 回测与参数优化
4.1 回测配置
回测是验证策略有效性的关键步骤。在qteasy中配置回测参数很简单:
# 配置回测参数
qt.config(
mode=1, # 回测模式
invest_start='20160101',
invest_end='20231231',
asset_type='E', # 股票
asset_pool=stock_pool,
trade_batch_size=100, # 100股整数倍交易
sell_batch_size=1, # 可以卖零股
trade_log=True # 记录交易日志
)
# 创建策略实例
strategy = FF3FactorStrategy(pars=(0.3, 0.3, 0.4))
# 创建交易员并运行回测
op = qt.Operator(strategy, signal_type='PT')
op.op_type = 'stepwise' # 逐步回测
op.set_blender('1.0*s0', "close") # 信号混合方式
op.run()
4.2 参数敏感性分析
参数优化是策略开发中最耗时的部分。我建议使用网格搜索法寻找最优参数组合:
from itertools import product
# 定义参数范围
size_params = [0.2, 0.3, 0.4]
bp_low_params = [0.2, 0.3, 0.4]
bp_high_params = [0.6, 0.7, 0.8]
best_params = None
best_annual_return = -np.inf
# 参数网格搜索
for params in product(size_params, bp_low_params, bp_high_params):
strategy = FF3FactorStrategy(pars=params)
op = qt.Operator(strategy)
op.run()
result = op.get_results()
annual_return = result['annualized_return']
if annual_return > best_annual_return:
best_annual_return = annual_return
best_params = params
print(f"最佳参数组合: {best_params}, 年化收益: {best_annual_return:.2%}")
在实际测试中,我发现(0.3, 0.3, 0.7)这个参数组合在A股市场表现最好,年化收益能达到40%以上。
5. 策略改进与风险控制
5.1 策略优化方向
基础的三因子策略虽然有效,但还有很大改进空间:
- 增加动量因子 :加入12个月动量因子可以显著提升策略表现
- 行业中性化 :对因子进行行业调整,避免行业集中风险
- 动态权重调整 :根据市场环境动态调整因子权重
# 行业中性化示例
def neutralize_by_industry(factor, industry):
"""对因子进行行业中性化处理"""
dummies = pd.get_dummies(industry)
model = LinearRegression()
model.fit(dummies, factor)
return factor - model.predict(dummies)
5.2 风险控制措施
高收益往往伴随着高风险,必须做好风险控制:
- 单只股票仓位限制 :不超过总资产的10%
- 最大回撤止损 :当回撤超过20%时暂停交易
- 流动性筛选 :剔除日均成交额低于1亿元的股票
# 流动性筛选示例
def filter_liquidity(stock_list, turnover_threshold=100000000):
"""筛选流动性好的股票"""
turnover = pro.daily_basic(ts_code=','.join(stock_list),
fields='ts_code,turnover_rate')
liquid_stocks = turnover[turnover['turnover_rate'] * mv >= turnover_threshold]
return liquid_stocks['ts_code'].tolist()
6. 实盘注意事项
将策略应用于实盘时,有几个关键点需要注意:
- 交易成本 :A股交易成本包括0.1%印花税(卖出)和约0.02%佣金,在回测中要计入
- 滑点控制 :实际成交价格可能与预期有差异,建议使用限价单
- 停牌处理 :遇到股票停牌要有备用方案
- 资金管理 :永远不要满仓,保留至少10%的现金
# 在qteasy中考虑交易成本
qt.config(
commission_rate=0.0002, # 佣金
tax_rate=0.001, # 印花税
slippage=0.001 # 滑点
)
在实际操作中,我建议先用小资金测试1-3个月,确认策略实际表现与回测一致后再加大投入。同时要定期(如每季度)重新评估策略表现,及时调整失效的因子。
更多推荐



所有评论(0)