一、问题的提出:因子表现为什么不稳定?
做量化的朋友应该都有这样的体会:一个因子上个月还能选出一堆牛股,这个月突然就不灵了。你以为是因子失效了?其实很可能只是市场风格变了。
打个比方,这就像开车——你在高速公路上可以踩油门开到120,但进了市区还这么开,那就是找事了。动量因子也是一样,牛市里追涨杀跌没问题,但到了震荡市或者熊市,这套玩法就容易踩坑。
问题在于,我们现在用的大部分因子都是"一根筋"的——不管市场是涨是跌,是疯狂还是恐慌,它们都用同一套逻辑在选股。这就像不管春夏秋冬都穿同一件衣服,肯定有时候会不合适。
所以我们需要让因子变得"聪明"一点,能够感知到市场的冷暖,知道什么时候该激进,什么时候该保守。这就是我们今天要聊的核心——构建对市场状态有感知能力的因子。
二、市场状态如何影响因子表现?
1. 什么是市场状态?它在投资中起什么作用?
市场状态可以理解为市场运行的“宏观环境”或“气候”,比如:
- 牛市:趋势明确向上,赚钱效应强,风险偏好高
- 熊市:趋势下行,避险情绪浓厚,动量失效
- 震荡市:趋势不明,风格频繁切换,套利难度高
在实际交易中,策略往往需要根据这些状态切换,比如:
- 牛市中适合追涨动量
- 熊市中更偏好防御风格
- 震荡市中可能偏向逆势套利或低波策略
问题是:当前的大多数因子,并不能“意识”到市场状态的变化,仍然一套模型跑到底。
2. 不同市场状态下因子表现的差异
将一个因子的历史表现分阶段统计:
- 在牛市阶段,它可能IC均值为 +0.08
- 在震荡市阶段,IC可能接近 0
- 在熊市阶段,甚至变成 -0.04
你就会发现:不是因子不行,而是“用错了时候”。
这种“因子效应的状态依赖性”是我们构建“市场状态感知因子”的动因所在。
三、如何构建市场状态感知因子?
1. 总体思路:将市场状态作为环境变量引入因子构建
我们不打算只是在事后做因子择时(组合层面的动态加权),而是要:
直接在因子构建阶段引入市场状态,让因子本身在不同市场下表现出差异化行为。
这就像是给因子加了一层“感知能力”,让它知道自己什么时候该强势,什么时候该收敛。
2. 构建步骤详解
0. 目标与范围
- 目标:让因子在不同市场环境下自适应,减少特征漂移,提升稳健性。
- 场景:截面选股(Rank/分位买入),周期通常为日频调仓。
1. 数据准备
- 标的:股票池(剔除 ST/停牌),指数(基准/行业/风格)。
- 频率:日线(β建议用周频收益估计更稳)。
- 对齐:对齐交易日,缺失值处理,复权校验。
2. 基础因子库
- 选择已有、可解释的基础因子(动量、反转、质量、估值、波动、资金等)。
- 先做去极值(winsorize)与必要的行业/市值中性化(也可在第7步统一做)。
3. 定义“市场状态” (可多条)
常用选项(建议z-score):
- 趋势:指数 20/40 日收益、EMA 斜率、趋势一致度(连阳/ADX)。
- 波动:市场年化波动率、隐含波动、近 60 日最大回撤。
- 广度/情绪:上涨家数占比、涨停−跌停、连板高度。
- 资金面:成交额偏离、北向净买入变化、两融余额变化。
- 风格:小盘−大盘、成长−价值 指数收益差。
稳定化:z-score、剪切(±3σ或分位)、设静区间( 时置 0)。
4. 估计个体敏感度 (模板A核心)
-
定义:滚动窗口 内,用个股周收益对市场周收益的 OLS 斜率:
-
建议:周频(5日)、;winsorize β;Var≈0 时沿用上期或置空。
-
可选:用相对 β()减少整体市场暴露漂移。
5. 构造调制量
- 基本式: 或 。
- 加静区间门控:若 ,令 。
6. 生成“状态感知因子”
- 核心式:
- 解释:同一市场状态被按β差异“打散”为因股而异的调制,改变截面排序。
7. 清洗与中性化(用于排序/建模前)
- 对 做:去极值 → 行业/市值中性化(可加对 β 的正交化)→ 截面 z-score。
- 顺序建议:构交互 → 清洗/中性化 → 排序/回归。
8. 回测与评估(必须做)
- 整体:IC/RankIC、IR、分位组收益、换手、成本后收益、回撤。
- 条件:按状态分组(牛/震荡/熊;高/低波;宽/窄广度)计算条件 IC/收益。
- Ablation:Base vs. (无β) vs. (模板A)。
- 鲁棒性:更换状态定义、窗口 、静区间 、剪切阈;结果是否一致。
- 防泄露:滚动窗口、时间分层 CV(按日期分组)。
9. 参数缺省值(起步就用)
- (周频点数);(状态 z-score 的静区间);
- 状态 z-score 窗口:252 日;剪切:±3σ;
- 回归/正交:Ridge 优先;行业暴露与市值对齐。
10. 进阶扩展(可选)
- 组层状态:行业/风格层级的 (跨组不同,天然改排序)。
- 对手因子混合:。
- 状态特异 β(A+):对波动/广度/资金等状态估“专属敏感度”。
- 学习型门控:(逻辑回归/GBDT)做连续门控(注意反泄露与正则)。
11. 最小可运行配方(Qlib/Pandas 口径)
Qlib YAML(核心字段)
RiW: "Div($close, Ref($close,5d)) - 1.0"
RmW: "Div($mkt_close, Ref($mkt_close,5d)) - 1.0"
Beta60: "Div(Cov(RiW, RmW, 60d), Var(RmW, 60d))"
MktRet20: "Div($mkt_close, Ref($mkt_close,20d)) - 1.0" # 可在外层做z-score与静区间
Mod: "Mul(Beta60, MktRet20)"
Base: "Div($close, Ref($close,60d)) - 1.0"
SA_Factor: "Mul(Base, Mod)"
pandas(极简示意)
# beta(周频、60期)
df['ri_w'] = df.groupby('sid')['close'].pct_change(5)
df['rm_w'] = df['mkt_close'].pct_change(5)
cov_im = df.groupby('sid')['ri_w'].rolling(60).cov(df['rm_w']).reset_index(level=0, drop=True)
var_m = df['rm_w'].rolling(60).var()
df['beta'] = cov_im / (var_m + 1e-12)
# s_t(指数20日收益→z-score),含静区间门控
s = df['mkt_close'].pct_change(20)
s = (s - s.rolling(252).mean())/(s.rolling(252).std()+1e-12)
df = df.assign(s_t=s).fillna(method='ffill')
df['gate'] = (df['s_t'].abs() >= 0.7).astype(float)
# 交互+清洗(示意)
df['m'] = (df['beta'] - df.groupby('date')['beta'].transform('mean')) * df['s_t'] * df['gate']
df['Base'] = df.groupby('sid')['close'].pct_change(60)
df['SA'] = df['Base'] * df['m']
# 后续:winsorize → 行业/市值中性 → 截面zscore → 回测
12. 常见坑与对策
- 只乘 s_t:不改排序 → 无效;务必加β 的个体差异或组层状态。
- 泄露:滚动窗口只用历史;特征/状态统一 Ref 到 t 当天可得。
- 过度翻转/噪音:加静区间、z-score、剪切,必要时平滑。
- 暴露失控:用相对β或对 β/行业/市值做一次正交化。
- 成本:显式计滑点与冲击,控制换手阈值。
一句话:“基础因子 ×(个体β × 市场状态)” 是最小可用的自适应方案;加上静区间、清洗与条件评估,你就能得到既有效又稳健的市场状态感知因子。
3. 方法推广与模型融合
- 可通过神经网络、树模型、注意力机制建模因子和状态之间的非线性交互
- 可用贝叶斯网络建模市场状态与策略间的因果影响
- 可结合行业状态、风格偏好、流动性指标作为补充环境变量
四、与传统“因子择时”的对比
项目 | 因子择时 | 市场状态感知因子 |
---|---|---|
所在层级 | 组合层(组合加权、选因子) | 因子层(因子本身结构) |
核心目标 | 预测因子未来收益 | 提高因子鲁棒性与适应性 |
调整机制 | 调整权重、动态换因子 | 动态调整因子信号强度 |
实现方式 | 时间序列模型、因子回归预测 | 交互项建模、条件因子设计 |
优势 | 把握周期节奏、时间溢价 | 抑制特征漂移、增强因子泛化能力 |
这两者是互补而非对立的关系,可以叠加使用。
五、小结与展望
构建“市场状态感知型因子”是一种全新的路径,它将市场环境纳入因子的构造逻辑之中,使因子具备了自适应性。
这种方法:
- 提高了因子在不同市场状态下的表现一致性
- 缓解了传统因子面临的“特征漂移”问题
- 能部分实现因子层面的择时能力
未来的研究可以进一步探索:
- 非线性交互建模(如 MLP、Transformer、GNN 等)
- 状态空间建模与贝叶斯优化
- 与因子组合优化、风格轮动系统的融合
- 多市场(A股、港股、ETF)跨域适应性研究