🏠
AI量化交易 · 第二十章
目录
CHAPTER 20

多因子策略

综合多个信号做决策——当单一因子不够用时,如何像招聘HR一样评估每只股票

本章导航

  1. 学习目标:你将带走什么
  2. 故事引入:当单一指标失灵时
  3. 核心知识:多因子策略的完整构建流程
  4. AI实践:与AI一起设计多因子模型
  5. 动手练习:Python实现多因子选股
  6. 本章测验
  7. 下一章预告

学习目标

欢迎来到第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:因子分类与选择

常见的股票因子可以归为以下几大类:

💡 点击展开:因子之间为什么会打架?

价值因子和动量因子经常互相矛盾。价值因子让你买跌得多的便宜货,动量因子让你买涨得好的强势股。2020年疫情后的美股市场就是一个典型案例:传统价值股(航空、能源、银行)跌得惨不忍睹,而成长股(科技、新能源)不断创新高。如果你只看价值因子,会错过整个牛市;如果你只看动量因子,可能在风格切换时被套在山顶。

正因为因子之间可能负相关,多因子组合才有分散风险的价值。但这也带来一个问题:当多个因子给出矛盾信号时,你该怎么综合?

Step 2:因子预处理——去极值与标准化

原始因子数据往往脏得可怕。以PE为例,亏损公司的PE为负数,极端情况下某些公司的PE可能高达几千甚至几万。如果不处理这些异常值,它们会严重扭曲综合评分。

业界常用的预处理方法有三种:

  1. 去极值(Winsorize):将因子值超过某百分位(如1%和99%)的数据截断到该百分位值。这就像给数据理发,去掉太长的极端值。
  2. 标准化(Standardize):将因子值转换为Z-score,即(原始值 - 均值)/ 标准差。这样不同因子的数值范围统一了,才能放在一起比较。
  3. 中性化(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月动量、波动率、换手率。下一步是给它们分配权重,合成一个综合评分。

最常见的加权方法有三种:

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}亿")

本章测验

测测你的理解
1. 为什么要对因子进行行业中性化处理?
  • A. 为了减少计算量
  • B. 避免组合过度偏配某些行业,使因子比较更公平
  • C. 因为行业信息没有用
  • D. 为了满足监管要求
不同行业的估值水平天然不同。不做行业中性化,价值因子会大量选中银行股,导致行业集中风险。
2. 如果一个因子的IC均值为0.02,IR为0.8,以下说法正确的是?
  • A. 这是一个非常强的因子,应该重仓使用
  • B. 这个因子完全没有预测能力
  • C. 预测能力较弱,但非常稳定,可作为辅助因子
  • D. IR高说明这个因子风险很大
IC均值0.02说明预测能力较弱,但IR=0.8说明IC波动很小、稳定性高。这类因子适合作为组合中的稳定器。
3. 等权加权多因子的主要优点是什么?
  • A. 简单直观,不易过拟合历史数据
  • B. 一定能获得最高收益
  • C. 不需要做任何因子检验
  • D. 可以自动适应市场变化
等权加权最大的优点是简单和稳健。复杂优化方法容易过拟合历史数据,而等权策略在样本外往往表现更稳定。

下一章预告

多因子策略让我们学会了综合多个信号做决策,但有一个问题我们还没解决:如何处理那些一次性、不可重复的事件?财报季、央行降息、突发地缘冲突、黑天鹅事件……这些事件无法被月度因子值捕捉,却能在短期内造成巨大的市场波动。

第21章将带你进入事件驱动策略的世界。我们将学习如何识别事件、分类事件、预测事件对市场的影响,并在事件窗口内布局交易。你还将亲手写一个财报超预期策略,看看如何用量化方法捕捉业绩公告带来的价格异动。

如果说多因子策略是日常吃饭,事件驱动策略就是节日大餐——不常有,但一旦抓住,回报丰厚。

觉得有收获?分享给同样对量化交易感兴趣的朋友

上一章
均值回归策略
高抛低吸的数学表达,布林带与配对交易
下一章
事件驱动策略
财报、政策、黑天鹅,捕捉一次性事件的超额收益

如果这本书对你有所启发,欢迎支持我继续创作

赞赏二维码