4. 单因子构建与测试(下):动量因子、质量因子、成长因子的构建与回测
上一节我们把价值因子和规模因子拆了个底朝天。这一节,咱们继续干剩下的三个:动量、质量、成长。这三个因子,说实话,在实战中比价值因子更「挑行情」。用好了,收益曲线漂亮得很;用不好,回撤能让你怀疑人生。
我个人习惯把这三个因子归为一类——「趋势与质地」因子。它们不像价值因子那样「捡烟蒂」,而是更关注股票本身的动能和基本面质量。嗯,咱们一个一个来。
4.1 动量因子:趋势是你的朋友,但别太晚下车
动量因子,说白了就是「强者恒强」。过去涨得好的股票,未来一段时间大概率继续涨。这个逻辑在A股有效吗?我做过测试,短期动量(1-3个月)在A股确实有效,但换手率极高,交易成本能吃掉大部分收益。
我建议用中期动量,也就是过去6-12个月的累计收益率。为什么?因为中期动量更稳定,不容易被短期噪音干扰。我在项目中遇到过,用3个月动量做轮动,结果频繁被「假突破」骗进去,手续费交得比收益还多。
动量因子的构建其实很简单:
import pandas as pd
import numpy as np
def calc_momentum_factor(df, window=252):
"""
计算动量因子:过去252个交易日的累计收益率
df: 包含'close'列的DataFrame
"""
# 计算日收益率
df['daily_ret'] = df['close'].pct_change()
# 滚动窗口累计收益率
df['momentum'] = (1 + df['daily_ret']).rolling(window=window).apply(
lambda x: np.prod(x) - 1, raw=True
)
# 注意:要跳过最近20个交易日,避免短期反转效应
df['momentum_clean'] = df['momentum'].shift(20)
return df
这里有个坑,我必须提醒你。动量因子天然存在「反转效应」——涨多了会跌。所以构建动量因子时,一定要跳过最近1个月的数据。我曾经没做这个处理,结果回测曲线漂亮得很,实盘一跑就崩。为什么?因为回测时用了未来信息,把短期反转当成了动量。
避坑指南:动量因子必须做「滞后处理」。我建议至少滞后20个交易日,把最近一个月的收益剔除掉。否则你抓到的不是动量,是「追涨杀跌」。
4.2 质量因子:好公司不等于好股票
质量因子,衡量的是公司的「质地」。ROE高、负债率低、盈利稳定——这些指标听起来很美。但我要泼一盆冷水:质量因子单独使用,效果其实一般。
为什么?因为市场已经给好公司定价了。你想想看,茅台ROE常年30%+,但它的股价已经反映了这个预期。质量因子的真正价值,在于和其他因子组合使用。比如质量+价值,或者质量+动量,效果会好很多。
我常用的质量因子组合:
def calc_quality_factor(df_financial):
"""
计算综合质量因子
包含:ROE、毛利率、资产负债率、盈利稳定性
"""
# ROE:净资产收益率
df_financial['roe'] = df_financial['net_profit'] / df_financial['equity']
# 毛利率
df_financial['gross_margin'] = df_financial['gross_profit'] / df_financial['revenue']
# 资产负债率(负向指标)
df_financial['debt_ratio'] = df_financial['total_liab'] / df_financial['total_assets']
# 盈利稳定性:过去5年ROE的标准差(越小越好)
df_financial['roe_stability'] = df_financial.groupby('stock_code')['roe'].rolling(20).std().values
# 综合打分:百分位标准化后等权相加
for col in ['roe', 'gross_margin']:
df_financial[f'{col}_rank'] = df_financial[col].rank(pct=True)
# 负债率反向排名
df_financial['debt_ratio_rank'] = 1 - df_financial['debt_ratio'].rank(pct=True)
# ROE稳定性反向排名
df_financial['roe_stability_rank'] = 1 - df_financial['roe_stability'].rank(pct=True)
# 等权合成
df_financial['quality_score'] = (
df_financial['roe_rank'] +
df_financial['gross_margin_rank'] +
df_financial['debt_ratio_rank'] +
df_financial['roe_stability_rank']
) / 4
return df_financial
个人经验:质量因子在熊市里特别管用。市场下跌时,资金会往「确定性」上靠。好公司的抗跌性明显强于垃圾股。我2018年做回测,质量因子多头的最大回撤比市场低了将近10个百分点。
4.3 成长因子:高增长背后的陷阱
成长因子,看的是营收增速、利润增速。这个因子很诱人——谁不喜欢高增长的公司呢?但我要告诉你,成长因子是「双刃剑」。
高增长的公司往往估值也高。一旦增速不及预期,股价会跌得很惨。我见过太多人追高成长股,结果业绩变脸,一天跌掉20%。所以成长因子一定要和估值因子配合使用。
成长因子的构建:
def calc_growth_factor(df_financial):
"""
计算成长因子
核心指标:营收增速、净利润增速、超预期增长
"""
# 营收同比增长率
df_financial['revenue_growth'] = df_financial.groupby('stock_code')['revenue'].pct_change(4)
# 净利润同比增长率
df_financial['profit_growth'] = df_financial.groupby('stock_code')['net_profit'].pct_change(4)
# 超预期增长:实际增速 vs 分析师预期
# 这里简化处理,用过去4个季度的平均增速作为预期
df_financial['expected_growth'] = df_financial.groupby('stock_code')['profit_growth'].rolling(4).mean().values
df_financial['surprise_growth'] = df_financial['profit_growth'] - df_financial['expected_growth']
# 合成成长因子
for col in ['revenue_growth', 'profit_growth', 'surprise_growth']:
df_financial[f'{col}_rank'] = df_financial[col].rank(pct=True)
df_financial['growth_score'] = (
df_financial['revenue_growth_rank'] +
df_financial['profit_growth_rank'] +
df_financial['surprise_growth_rank']
) / 3
return df_financial
核心要点:成长因子最怕「增速拐点」。一个公司从50%增速降到30%,虽然还是高增长,但股价可能腰斩。所以做成长因子时,我建议关注「增速变化的方向」,而不仅仅是增速的绝对值。
4.4 三因子回测框架
好了,三个因子都构建完了。咱们把它们放到一个回测框架里跑一跑。我习惯用分层回测法——把股票按因子值分成10组,看多空组合的表现。
def factor_backtest(df, factor_col, n_groups=10):
"""
单因子分层回测
"""
results = []
for date in df['date'].unique():
df_date = df[df['date'] == date].copy()
# 按因子值分组
df_date['group'] = pd.qcut(df_date[factor_col], n_groups, labels=False)
# 计算每组下期收益
for g in range(n_groups):
group_stocks = df_date[df_date['group'] == g]['stock_code']
next_ret = df[df['stock_code'].isin(group_stocks) &
(df['date'] == next_date)]['return'].mean()
results.append({
'date': date,
'group': g,
'return': next_ret
})
return pd.DataFrame(results)
跑完回测,你会发现一个有意思的现象:动量因子在牛市中表现最好,质量因子在熊市中抗跌,成长因子在震荡市中容易「翻车」。这就是为什么我说,没有万能的因子,只有合适的场景。
我的建议:别单独用任何一个因子。把动量、质量、成长组合起来,做一个「复合因子」。比如动量占40%,质量占30%,成长占30%。这样在不同市场环境下,总有一个因子在起作用。
4.5 本章小结
这一节我们干了三件事:
- 动量因子:中期动量有效,记得滞后处理
- 质量因子:单独用效果一般,组合用才是王道
- 成长因子:高增长是双刃剑,小心增速拐点
嗯,这三个因子构建起来不难,难的是怎么用。我见过太多人把因子库建得满满当当,结果实盘一跑就亏钱。为什么?因为因子之间会互相干扰,组合方式不对,反而会放大风险。
下一节,咱们聊聊因子组合的「化学反应」——怎么把多个因子合成一个有效的选股模型。到时候我会分享一个我用了三年的组合框架,保证让你少走弯路。