本章导航
- 学习目标:你将带走什么
- 故事引入:当单一指标失灵时
- 核心知识:多因子策略的完整构建流程
- AI实践:与AI一起设计多因子模型
- 动手练习:Python实现多因子选股
- 本章测验
- 下一章预告
学习目标
欢迎来到第20章。如果你已经跟着本书一路走到这里,那么你已经掌握了趋势跟踪、均值回归、均线交叉等经典策略。但现实世界从不按单一规则出牌——当MACD金叉时,RSI可能超买;当布林带收窄时,成交量可能萎缩。单一因子就像用一只眼睛看路,总有盲区。
本章结束时,你将能够:
- 理解因子投资的核心思想:为什么多因子比单因子更稳健,以及如何量化这种稳健性
- 掌握因子筛选与标准化方法:从数百个候选因子中筛选出真正有预测力的因子
- 构建一个完整的多因子评分模型:从数据获取、因子计算、权重分配到组合构建的全流程
- 识别因子失效的信号:当曾经有效的因子突然罢工时,你如何提前察觉
故事引入:老王的估值陷阱
老王是个坚定的价值投资者。他只看一个指标:市盈率(PE)。逻辑很简单——便宜的就是好的。2018年,他用这个策略筛选出了A股市场PE最低的一批股票,满仓杀入。结果你猜怎么着?那批股票里有三分之一是银行股,另外三分之一是陷入经营困境的周期股。
一年过去,沪深300跌了约25%,老王的组合跌了35%。他困惑不已:我不是买了最便宜的股票吗?为什么跌得更惨?
问题出在哪里?老王只看了估值这一个维度,却忽略了另一个关键事实:很多低PE股票之所以便宜,是因为它们的盈利能力(ROE)极差,或者资产负债率高企,或者行业正处于下行周期。单一因子给出的信号,往往是片面的、有偏的,甚至是危险的。
核心知识:多因子策略的完整构建流程
什么是因子?从生活化比喻说起
在量化交易中,因子(Factor)是指能够解释或预测资产收益的特征变量。你可以把它理解为影响股票涨跌的基因。
举个生活化的例子:假设你要预测一个人未来十年的收入水平。单一指标可能有很大偏差——名校毕业的人未必高收入,但如果我们综合考虑学历、行业、工作年限、专业技能、人脉资源、身体健康状况等多个因子,预测的准确度就会大幅提升。
股票市场同理。Fama-French在1993年提出的三因子模型开创了学术界对因子投资的系统性研究。他们发现,除了市场整体收益(Beta)之外,市值因子(SMB)和价值因子(HML)也能显著解释股票收益的横截面差异。此后几十年,学术界陆续发现了动量因子、质量因子、低波动因子、红利因子等数百个候选因子。
多因子策略的流程架构
flowchart TD
A[选择候选因子池] --> B[因子数据计算]
B --> C[因子预处理去极值标准化中性化]
C --> D[因子有效性检验 IC/IR/分层回测]
D --> E{筛选有效因子}
E -->|有效| F[确定因子权重]
E -->|无效| G[剔除或优化]
F --> H[计算综合得分]
H --> I[按得分排序选股]
I --> J[构建投资组合]
J --> K[动态监控与再平衡]
Step 1:因子分类与选择
常见的股票因子可以归为以下几大类:
- 价值因子:PE、PB、PS、PCF、股息率等。核心逻辑是便宜有好货——市场有时会过度悲观,把优质资产打到不合理的低价。
- 质量因子:ROE、ROA、毛利率、净利率、盈利稳定性等。核心逻辑是好公司值得溢价——持续高盈利的企业往往有护城河。
- 动量因子:过去1个月/3个月/6个月/12个月的收益率。核心逻辑是趋势延续——涨得好的股票往往继续涨(短期反转除外)。
- 波动因子:标准差、贝塔系数、最大回撤等。核心逻辑是低风险异象——低波动股票长期收益反而更高。
- 流动性因子:换手率、Amihud非流动性指标等。核心逻辑是流动性溢价——流动性差的资产需要额外收益补偿。
- 情绪因子:分析师预期、盈余惊喜、资金流向等。核心逻辑是信息不对称——先知先觉的资金往往走在前面。
价值因子和动量因子经常互相矛盾。价值因子让你买跌得多的便宜货,动量因子让你买涨得好的强势股。2020年疫情后的美股市场就是一个典型案例:传统价值股(航空、能源、银行)跌得惨不忍睹,而成长股(科技、新能源)不断创新高。如果你只看价值因子,会错过整个牛市;如果你只看动量因子,可能在风格切换时被套在山顶。
正因为因子之间可能负相关,多因子组合才有分散风险的价值。但这也带来一个问题:当多个因子给出矛盾信号时,你该怎么综合?
Step 2:因子预处理——去极值与标准化
原始因子数据往往脏得可怕。以PE为例,亏损公司的PE为负数,极端情况下某些公司的PE可能高达几千甚至几万。如果不处理这些异常值,它们会严重扭曲综合评分。
业界常用的预处理方法有三种:
- 去极值(Winsorize):将因子值超过某百分位(如1%和99%)的数据截断到该百分位值。这就像给数据理发,去掉太长的极端值。
- 标准化(Standardize):将因子值转换为Z-score,即(原始值 - 均值)/ 标准差。这样不同因子的数值范围统一了,才能放在一起比较。
- 中性化(Neutralize):去除因子与行业、市值等已知风险的关联。例如,银行股天然PE低、ROE稳定,如果不做行业中性化,价值因子会过度偏配金融板块。
import numpy as np
import pandas as pd
def winsorize(series, lower=0.01, upper=0.99):
q_low = series.quantile(lower)
q_high = series.quantile(upper)
return series.clip(q_low, q_high)
def standardize(series):
return (series - series.mean()) / series.std()
def neutralize(factor, market_cap, industry_dummy):
from sklearn.linear_model import LinearRegression
X = pd.concat([np.log(market_cap), industry_dummy], axis=1)
model = LinearRegression().fit(X, factor)
residual = factor - model.predict(X)
return standardize(residual)
# 示例:对PE因子进行完整预处理
pe_raw = df['pe_ratio']
pe_clean = winsorize(pe_raw)
pe_std = standardize(pe_clean)
pe_neutral = neutralize(pe_std, df['market_cap'], df[['is_bank', 'is_tech']])
Step 3:因子有效性检验
不是所有看起来合理的因子都真的有效。你可能花大量时间计算了一个CEO星座因子(根据CEO的星座选股),结果回测发现它纯属噪声。如何科学评估因子质量?
IC(信息系数)是最常用的指标。它衡量因子值与未来收益之间的秩相关性(Spearman相关系数)。IC = 0.05就已经算是一个不错的因子,IC大于0.1则是神仙因子。但要注意,IC的稳定性比绝对值更重要——一个IC均值0.08但波动很小的因子,往往比IC均值0.15但忽正忽负的因子更可靠。
IR(信息比率) = IC均值 / IC标准差。IR大于0.5的因子通常被认为具有稳定的预测能力。
from scipy.stats import spearmanr
def calc_ic(factor, forward_return):
ic, _ = spearmanr(factor, forward_return)
return ic
def分层回测(df, factor_col, return_col='forward_1m_return', n_groups=5):
df['group'] = pd.qcut(df[factor_col], n_groups, labels=False)
group_returns = df.groupby('group')[return_col].mean()
return group_returns
# 计算某因子过去12个月的月度IC
ics = []
for month_end in pd.date_range('2020-01', '2020-12', freq='M'):
snapshot = df[df['date'] == month_end]
ic = calc_ic(snapshot['factor_value'], snapshot['forward_return'])
ics.append(ic)
print(f"IC均值: {np.mean(ics):.3f}")
print(f"IR: {np.mean(ics)/np.std(ics):.3f}")
Step 4:因子加权——如何合成综合得分
假设你筛选出了5个有效因子:PE(价值)、ROE(质量)、1月动量、波动率、换手率。下一步是给它们分配权重,合成一个综合评分。
最常见的加权方法有三种:
- 等权加权:每个因子权重相同(20%)。简单、直观、不易过拟合,但可能把有效因子和边际因子等同对待。
- IC加权:按因子的历史IC表现分配权重,IC高的因子权重更大。这听起来很合理,但问题在于历史IC不一定能预测未来IC。
- 风险平价加权:让各因子对组合风险的贡献相等。适合因子波动差异大的场景。
Step 5:组合构建与动态监控
有了综合评分后,选股变得简单:按得分从高到低排序,取前N只股票构建组合。但这还不够——你需要决定持仓数量、再平衡频率、权重分配方式。
更重要的是因子监控。因子不是永远有效的——2017年A股漂亮50行情让小市值因子失效,2021年核心资产泡沫破裂让质量因子遭遇大幅回撤。你需要建立一套因子健康度仪表盘,监控各因子的近期IC、换手率、相关性变化,一旦发现某个因子持续罢工,就要考虑降低权重或暂时剔除。
def build_multi_factor_portfolio(df, factor_weights, n_stocks=50):
df['score'] = 0
for factor, weight in factor_weights.items():
df['score'] += df[factor] * weight
selected = df.nlargest(n_stocks, 'score')
selected['weight'] = 1.0 / n_stocks
return selected[['code', 'name', 'score', 'weight']]
weights = {
'pe_z': -0.20, 'pb_z': -0.10, 'roe_z': 0.25,
'momentum_3m_z': 0.20, 'volatility_z': -0.15, 'turnover_z': -0.10
}
portfolio = build_multi_factor_portfolio(stock_pool, weights, n_stocks=100)
与AI一起练习
练习1:让AI帮你筛选有效因子
Prompt模板:
我有一份A股全市场股票的月度数据,包含PE_TTM、PB、ROE_TTM、毛利率、过去1月/3月/6月收益率、20日波动率、20日平均换手率。
请帮我写Python代码:
1. 计算每个因子的月度IC(与未来1个月收益率的Spearman相关系数)
2. 画出IC的时间序列图和分布直方图
3. 筛选出IC均值>0.03且IR>0.3的因子
4. 检查筛选后因子的两两相关性,剔除高度冗余的因子
练习2:让AI设计一个三因子策略
请用Python和Backtrader实现一个多因子选股策略:
- 每月初调仓,范围沪深300成分股
- 因子1(价值):PE_TTM倒数标准化,权重30%
- 因子2(质量):ROE_TTM标准化,权重40%
- 因子3(动量):过去60日收益率(排除最近20日),权重30%
- 每个因子做市值和行业中性化
- 选综合得分最高的30只,等权配置
- 回测2018-01-01至2023-12-31
- 输出年化收益、夏普比率、最大回撤
练习3:让AI分析因子失效的原因
小市值因子在2017年之前表现极好,但2017年后持续失效甚至反转。
请分析:
1. 从市场结构、投资者结构、政策环境解释失效原因
2. 写Python代码检测该因子的结构断裂点
3. 提出自适应因子权重方案:当因子近期IC为负时自动降权
动手练习:完整的多因子策略代码
"""多因子选股策略 - 简化版 需要: pip install akshare pandas numpy"""
import akshare as ak
import pandas as pd
import numpy as np
def get_stock_data():
df = ak.stock_zh_a_spot_em()
df = df[['代码','名称','最新价','市盈率-动态','市净率',
'总市值','换手率','涨跌幅']].copy()
df.columns = ['code','name','price','pe','pb',
'market_cap','turnover','change_pct']
df = df[df['pe'] > 0]
df = df[df['market_cap'] > 1e8]
return df
def calc_factors(df):
df['ep'] = 1 / df['pe']
df['bp'] = 1 / df['pb']
df['liq'] = -df['turnover']
for col in ['ep','bp','liq']:
df[col] = df[col].clip(df[col].quantile(0.01),
df[col].quantile(0.99))
df[f'{col}_z'] = (df[col]-df[col].mean())/df[col].std()
return df
def build_portfolio(df, n=50):
df['score'] = df['ep_z']*0.4 + df['bp_z']*0.4 + df['liq_z']*0.2
selected = df.nlargest(n, 'score')
selected['weight'] = 1.0 / n
return selected
if __name__ == '__main__':
raw = get_stock_data()
factored = calc_factors(raw)
portfolio = build_portfolio(factored, n=50)
print("=== 多因子组合持仓 ===")
print(portfolio[['code','name','score','weight']].head(10))
print(f"\n组合平均PE: {portfolio['pe'].mean():.2f}")
print(f"组合平均PB: {portfolio['pb'].mean():.2f}")
print(f"组合平均市值: {portfolio['market_cap'].mean()/1e8:.2f}亿")
本章测验
下一章预告
多因子策略让我们学会了综合多个信号做决策,但有一个问题我们还没解决:如何处理那些一次性、不可重复的事件?财报季、央行降息、突发地缘冲突、黑天鹅事件……这些事件无法被月度因子值捕捉,却能在短期内造成巨大的市场波动。
第21章将带你进入事件驱动策略的世界。我们将学习如何识别事件、分类事件、预测事件对市场的影响,并在事件窗口内布局交易。你还将亲手写一个财报超预期策略,看看如何用量化方法捕捉业绩公告带来的价格异动。