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 数据获取与处理

获取数据是量化交易的第一步,也是最容易踩坑的环节。我总结了几点经验:

  1. 股票池选择 :建议使用沪深300成分股,流动性好,数据质量高
  2. 数据清洗 :特别注意处理停牌股票和异常值
  3. 因子计算 :账面市值比=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 策略优化方向

基础的三因子策略虽然有效,但还有很大改进空间:

  1. 增加动量因子 :加入12个月动量因子可以显著提升策略表现
  2. 行业中性化 :对因子进行行业调整,避免行业集中风险
  3. 动态权重调整 :根据市场环境动态调整因子权重
# 行业中性化示例
def neutralize_by_industry(factor, industry):
    """对因子进行行业中性化处理"""
    dummies = pd.get_dummies(industry)
    model = LinearRegression()
    model.fit(dummies, factor)
    return factor - model.predict(dummies)

5.2 风险控制措施

高收益往往伴随着高风险,必须做好风险控制:

  1. 单只股票仓位限制 :不超过总资产的10%
  2. 最大回撤止损 :当回撤超过20%时暂停交易
  3. 流动性筛选 :剔除日均成交额低于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. 实盘注意事项

将策略应用于实盘时,有几个关键点需要注意:

  1. 交易成本 :A股交易成本包括0.1%印花税(卖出)和约0.02%佣金,在回测中要计入
  2. 滑点控制 :实际成交价格可能与预期有差异,建议使用限价单
  3. 停牌处理 :遇到股票停牌要有备用方案
  4. 资金管理 :永远不要满仓,保留至少10%的现金
# 在qteasy中考虑交易成本
qt.config(
    commission_rate=0.0002,  # 佣金
    tax_rate=0.001,  # 印花税
    slippage=0.001  # 滑点
)

在实际操作中,我建议先用小资金测试1-3个月,确认策略实际表现与回测一致后再加大投入。同时要定期(如每季度)重新评估策略表现,及时调整失效的因子。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐