市场状态感知因子:方法论与实现指南(以动量 & 波动率为例)
前言
- 上一篇《自适应市场状态感知因子的构建》的文章中,我们提出了在因子层面构建市场状态感知因子的思路,不同于在模型层面对因子权重动态调整的因子择时方法,我们的设计思路希望在因子构建层面能够动态感知市场状态的变化。本篇研究继续上篇文章的思路,构建方法层面更加严谨,特别是动量因子及其线性组合形式的衍生因子,可以参照CAMP形式的构建方式构建具备市场动量感知的因子。虽然不是严格的数学推到,但是也尽量从逻辑层面尽量严谨。本篇主要以动量因子为例,并使用qlib的进行结果验证和分析,在进行不同股票池组合和参数(20日动量对未来5日收益,5日动量对未来1日收益)分析,结果显示,市场动量感知的因子相对于原始动量因子,RankIC,RankIC_IR及T检验结果都明显得到提升。
0. 摘要(TL;DR)
- 目标:在截面上构建能感知市场状态(趋势、波动、流动性、广度等)的交易因子,兼顾区分度与稳健性。
- 核心做法:将个股信号 Sᵢ,ₜ 与市场级状态信号 Sₘ,ₜ 建立耦合刻画(线性回归β或非线性互信息 MI),把信号拆为:
- 残差分量(去市场化的纯个股成分)
- 状态分量(耦合强度 × 市场状态)
融合后得到最终的状态感知因子。 - 动量(线性):CAPM 形态线性结构可“传递”;强烈推荐用回归β + 残差化 + 状态项。
- 波动(非线性):用 log σ 的弹性(γ)或互信息刻画耦合;“低特异波 + 状态惩罚/门控”更稳。
- 合成:最简单有效的基线是 等权合成:
z(残差) + z(状态分量)
,无需调参。
1. 基础原理与术语对齐
1.1 由 CAPM 推得的“线性动量—市场动量”关系
目标:用 CAPM 的原理解释——当“动量”被定义为对收益的同一线性滤波时,个股动量与市场动量之间存在与 CAPM 同形(或严格一致)的线性关系;据此引入“市场动量 β”构建市场状态感知的动量因子在原理上与 CAPM 一致/相似。
假设(A)
- A1(CAPM 基式):对超额收益(省略 Rf)有
- A2(线性滤波):定义线性动量为对收益的同一线性算子 L(固定权重,不依赖数据)
(例如“12–2”动量:𝒦=\2,dots,12\,wₖ=1;或 EMA‐mom 等。)
- A3(稳定性):在用于估计的滚动窗口内,α_i,βᵢ 可视为常数(或缓慢变化)。
- A4(弱条件独立):\ε_i,t\ 对市场收益 \Rₘ,ₜ\ 不相关;线性算子 L 与收益过程独立。
命题(P)
在(A1–A4)下,对任意确定性的线性算子 L,存在
其中
证明要点(略):线性算子可穿透到和式内:
即得 Sᵢ,ₜ=aᵢ+βᵢ Sₘ,ₜ+uᵢ,ₜ。由(A1,A4)有 Cov(uᵢ,ₜ,Sₘ,ₜ)=0。
推论(C1:回归等价)
用 OLS 在滚动窗内回归
得到的斜率估计 b̂_i 在大样本下一致收敛于 βᵢ;截距 â_i 收敛于 aᵢ。
注:若使用超额收益且 Σₖ w_k=0(零和权重,如差分型滤波),则 aᵢ=0。
推论(C2:与 CAPM 的一致/相似性)
- CAPM 期望收益:E[Rᵢ]=α_i+βᵢ E[Rₘ]。
- 线性动量的期望:E[Sᵢ]=aᵢ+βᵢ E[Sₘ]。
两式形式一致:把“收益—市场收益”替换为“线性动量—市场线性动量”,斜率仍为 βᵢ。因此,以 βᵢ 刻画个股对市场动量的敏感度,在原理上与 CAPM 对收益的敏感度刻画一致/同形。
在此基础上的状态感知动量因子
- 残差动量(纯个股成分):
- 状态分量(市场状态映射到截面):取市场动量的标准化值 Mₜ=z(Sₘ,ₜ),令
由于 b̂_i 在截面上异质,当 Mₜ 显著(强趋势/强反向)时,Cᵢ,ₜ 给予“顺/逆周期”个股差异化打分。
- 合成(零参基线):
这与条件 CAPM/条件因子模型相似:在给定状态 Mₜ 下,暴露 βᵢ 把市场状态投射到个股层面;同时保留“与市场近似正交”的残差成分用于截面区分。
边界与注意
- βᵢ 时变:若 βᵢ(t) 在窗内快速变动,则 b̂_i 估计的是“加权平均 β”;可用短窗+收缩/Kalman 滤波平滑。
- 非线性信号(如 RSI、波动率):不满足线性算子条件,上述同构不再成立;此时宜用“因子收益回归”或“互信息/非参条件期望”刻画耦合。
- 指数由成分构成:为防机械相关,构造 Sₘ,ₜ 时对每只股票使用 leave-one-out 版本。
- 截距项:除非在窗内去均值或选 Σ wₖ=0 的滤波,否则必须保留截距,避免把均值差错压进斜率。
- 统计推断:动量窗重叠带来自相关,标准误宜用 HAC/Newey–West。
小结:
当动量被定义为对收益的同一线性滤波时,个股动量—市场动量满足与 CAPM 同形的线性关系,其斜率即个股的市场动量 β。据此构造的“残差动量 + β×市场状态”的状态感知动量因子,在原理层面与(条件)CAPM 的敏感度—溢价刻画保持一致/相似的逻辑结构。
1.2 非线性信号
- RSI、波动率(涉及平方/开方/比值)、ATR 等属于非线性,不保留上述线性同构。
- 可改用:
- 因子收益回归(收益对“可交易因子收益”的回归);
- 互信息/非参条件期望刻画耦合强度与去市场化。
1.3 β、相关系数 ρ、互信息 MI
- βᵢ=Cov(Sᵢ,S_m)/Var(Sₘ)(含截距);边际敏感度,有方向与尺度。
- ρᵢ=Cov(Sᵢ,S_m)/(σᵢσₘ);无量纲依赖强度,难以残差化。
- 互信息 I(Sᵢ;Sₘ)≥ 0;连续情形无固定上界,常用归一化 NMI ∈[0,1]。
2. 状态感知因子的统一构造框架
输入:个股信号 Sᵢ,ₜ、市场状态信号 Sₘ,ₜ(同频率/同口径;指数建议用 leave-one-out 版本)。
-
耦合估计(滚动,仅用历史样本)
- 线性:Sᵢ,τ=aᵢ+bᵢ Sₘ,τ+uᵢ,τ⇒ â_i,b̂_i。
- 非线性:估计 NMIᵢ;或非参回归得 Ŝ_i|m,t=E[Sᵢ|Sₘ]。
-
两路分量(当期 t)
- 残差分量:线性 Uᵢ,ₜ=Sᵢ,ₜ-â_i-b̂_i Sₘ,ₜ;非线性 Uᵢ,ₜ=Sᵢ,ₜ-Ŝ_i|m,t。
- 状态分量:Cᵢ,ₜ=b̂_i(t)· Mₜ;或 (1-lambda NMIᵢ)· Sᵢ,ₜ· g(Mₜ)。
-
融合与后处理
- 零参基线(推荐):
F = z(U) + z(C)
(等权);或门控z(U) + min(1, |z(M)|)*z(C)
。 - Winsorize(截面 1%/99%)→ z-score(逐日)→ 行业中性(逐日哑元回归)。
- 零参基线(推荐):
评估:整体与分状态桶的 IC/RankIC、滚动稳定性、与基准/FF 因子正交性、交易可实现性(T+1、涨跌停、冲击成本)。
3. 动量因子:线性模型的状态感知实现(示范)
3.1 信号定义(线性动量)
- 个股:Sᵢ,ₜ=Σₖ=₂₂^252 Rᵢ,ₜ-ₖ(近似 12–2,跳过最近 1 个月)。
- 市场:同窗同权重构造指数信号 Sₘ,ₜ。
3.2 耦合估计(含截距)
- 窗口:日频 252 天 / 月频 36–60 月。
- 推断:重叠窗 → HAC/Newey–West。
- 截距项必要:避免 β 偏误。
3.3 残差与状态分量
- 残差动量:Uᵢ,ₜ=Sᵢ,ₜ-â_i-b̂_i Sₘ,ₜ。
- 市场状态:Mₜ=z(Sₘ,ₜ)(或其他维度的综合状态)。
- 状态分量:Cᵢ,ₜ=b̂_i(t)· Mₜ。
3.4 融合与后处理(零参基线)
3.5 参考实现
- 脚本:
state_aware_momentum_demo.py
state_aware_momentum_20d_csi300.ipynb
。 - 产出:使用qlib框架分析
universe | mode | factor | RankIC_mean | RankIC_std | RankIC_IR | NW_t |
---|---|---|---|---|---|---|
csi300 | W20_H5 | Raw | -0.0139653977976278 | 0.2068022966771483 | -0.067530187149856 | -1.356607554290438 |
csi300 | W20_H5 | State | -0.0203683130708901 | 0.217624600937486 | -0.0935937986015701 | -1.747277870589656 |
csi300 | W5_H1 | Raw | -0.0123293163115737 | 0.2067349330720816 | -0.0596382823568327 | -2.271284119850466 |
csi300 | W5_H1 | State | -0.0146931821223685 | 0.2257955564110997 | -0.065072946323253 | -2.3597968703572527 |
csi500 | W20_H5 | Raw | -0.0223919813592998 | 0.1835834587540075 | -0.1219716716924042 | -2.3911226262704286 |
csi500 | W20_H5 | State | -0.0294257836685261 | 0.1973470005486626 | -0.1491068198995516 | -2.722705323714508 |
csi500 | W5_H1 | Raw | -0.0152123269165847 | 0.1799970316763168 | -0.0845143210135853 | -3.1243417967709197 |
csi500 | W5_H1 | State | -0.0194024112111173 | 0.2045190746849085 | -0.0948684676038632 | -3.224777842358672 |
csi1000 | W20_H5 | Raw | -0.0392538908078415 | 0.1615883415025022 | -0.242925265788644 | -4.863811863661362 |
csi1000 | W20_H5 | State | -0.0391027833587333 | 0.1795817536498932 | -0.2177436324347677 | -3.96008297408208 |
csi1000 | W5_H1 | Raw | -0.022638661810779 | 0.1682451559729561 | -0.1345575846143112 | -5.167444084613214 |
csi1000 | W5_H1 | State | -0.0241283614337777 | 0.2015436883807983 | -0.1197177724969955 | -4.063402400113424 |
-结果分析:对比各分组测试结果,使用市场状态感知的动量因子相对于原始动量因子,器RankIC_mean明显提升,RankIC_IR同样有明显提升,且统计有效性NW_t检验的结果保持稳定,部分分组结果也有提升。市场状态感知的动量因子相对于原始动量因子有更好的表现。
4. 波动率因子:非线性场景的状态感知
4.1 信号定义(建议 log 变换)
- 个股波动:实现方差/标准差、GK/Parkinson 等;令 Sᵢ,ₜ=log σᵢ,ₜ。
- 市场波动:同口径 Sₘ,ₜ=log σₘ,ₜ。
4.2 耦合刻画
- 弹性(波动β):Sᵢ,τ=cᵢ+γ_i Sₘ,τ+uᵢ,τ⇒ γ̂_i。
- 互信息:NMIᵢ(Sᵢ,S_m) 捕捉非线性耦合。
4.3 构造与方向
- 残差波动:Uᵢ,ₜ=Sᵢ,ₜ-ĉ_i-γ̂_i Sₘ,ₜ。
- 状态变量:Vₜ=z(Sₘ,ₜ) 或高/低波档位。
- 交易倾向:常用偏好低特异波与高波期惩罚高耦合:
5. 市场状态变量库(选哪个,就感知哪个)
- 趋势:指数动量 z-score、TSMOM 收益。
- 波动:log σₘ、VIX/中证波动率的 z-score。
- 流动性:换手率/成交额 z-score、Amihud(取负)。
- 广度:上涨家数占比、%成份股站上 MA、AD Line。
- 相关性/单因子主导:平均两两相关、PCA 第一主成分解释度。
- 合成状态:对以上标准化后做 PCA/加权得 Mₜ。
注意:指数由成分构成时,个股耦合估计应使用 leave-one-out 的 Sₘ,ₜ^(-i)。
6. 合成权重:不调参也能稳的三种方式
-
等权(零参基线,推荐)
F=z(U)+z©。 -
状态强度门控
F=z(U)+min(1,|z(M)|) z©。 -
表现驱动(轻量自适应)
用过去 W 期各自的 RankIC 或反波动做权重:
F=wU z(U)+wC z©,其中 wU,w_C 由历史窗归一化。
7. 评估与回测清单
- IC/RankIC:整体与“高/低趋势、高/低波”状态桶。
- 分组单调性:按分位构组合,观察收益曲线与换手。
- 稳定性:滚动 12M IC、状态切换鲁棒性。
- 正交性:对 MKT、SMB/HML/行业做回归,关注 α 与系数显著性。
- 可实现性:T+1、涨跌停、调仓频次/滞后、冲击成本,设置换手上限。
8. 实操细节与常见坑
- 截距项要保留(或在窗内先去均值再过原点,等价)。
- 仅用历史数据估计(滚动窗截断到 t-1),避免泄露。
- 重叠窗口 → 显著性用 HAC/Newey–West;MI 用 block-permutation 做显著性。
- 收缩与平滑:对 b̂_i,γ̂_i 做行业层面 James–Stein 收缩或指数平滑。
- 极端市场日:限制 |z(M)| 或剪裁状态项,防过度放大。
- 数据处理:缺失值/停牌填充、涨跌停对收益与可交易性的影响。
9. 极简伪代码(动量线性版)
# 构造线性动量(12–2 近似)
S_i = rolling_sum(returns_i.shift(21+1), window=252-21)
S_m = rolling_sum(index_ret.shift(21+1), window=252-21)
# 滚动β与截距(协方差/方差闭式)
beta_i, intercept_i = rolling_ols_via_covvar(S_i, S_m, window=252)
# 残差与状态分量
U = S_i - intercept_i - beta_i * S_m
M = zscore_over_time(S_m) # 市场趋势状态
C = beta_i * M
# 等权合成 + 行业中性
F = zscore_xs(winsorize_xs(U)) + zscore_xs(winsorize_xs(C))
F = industry_neutralize_xs(F, industry_labels)