多因子投资有个隐藏前提:你先决定持有哪几个因子(价值、动量、质量……),再永远等权持有它们。但因子不是静止的——价值会沉寂三年、动量会接力领涨、低波会在泡沫里失效。如果「因子」和「股票」一样是一种资产,那它也应该有自己的择时信号。
本文的论点:把因子当成可交易的资产,用**因子动量(Factor Momentum)**去配置它们——哪类因子过去走强,就超配以它;走弱就减配。在 25 年(300 个月)合成的 5 个风格因子上,静态等权持有全部因子的 Sharpe 是 0.94;而用过去 12 个月收益做信号的因子动量组合,Sharpe 冲到 2.27、回撤 −17.3%(比静态 −21.2% 还小)。更关键的是,它和共同市场的相关只有 0.18——它赚的是「因子之间轮动」的钱,不是市场方向的钱。
这听起来像把「股票动量」简单搬了一层,但二者有本质区别:股票动量赚的是「别的股票比你还惨/更强」的横截面相对收益;因子动量赚的是「这类风险溢价自身正处在强势 regime」的时间序列状态切换。后者要求因子的风险溢价本身具有可预期的持续性,而不是靠和其他因子比谁更弱。正因如此,因子动量不是截面多空的替代品,而是架在「因子选择」之上的一层独立择时——它和底层因子收益相关却不等于它们,这正是下文 0.72 相关的来源。

一、为什么因子会有动量#
因子动量成立的前提只有一个:因子的收益是可预测的、且有持续性。这和我们做股票截面动量是一个道理,只是把对象从「股票」换成了「因子」。
我们构造 5 个风格因子的月度收益,每个因子的收益由三部分组成:
μ_i:因子的长期漂移(价值、动量各自有不同的正溢价);s_{i,t}:一个 AR(1) 慢变状态(持续性φ=0.85)——这是因子「时好时坏」的来源,也是动量能捕捉的对象;β_i·M_t+ 特异噪声:共同市场暴露与特异冲击。
import numpy as np
NF = 5
NAMES = ["价值", "动量", "规模", "质量", "低波"]
T = 300 # 25 年月度
mu = np.array([0.0075, 0.0085, 0.0060, 0.0070, 0.0055]) # 月度漂移
sig = np.array([0.034, 0.038, 0.040, 0.030, 0.026]) # 特异波动
phi = 0.85 # 因子状态持续性
beta = np.array([0.25, 0.40, 0.55, 0.20, -0.30]) # 对共同市场暴露
rng = np.random.default_rng(20260712)
s = np.zeros((NF, T))
for i in range(NF):
s[i, 0] = rng.normal(0, sig[i] * 0.5)
for t in range(1, T):
s[i, t] = phi * s[i, t - 1] + rng.normal(0, sig[i] * np.sqrt(1 - phi ** 2))
M = rng.normal(0, 0.030, T)
noise = rng.normal(0, 1, (NF, T)) * sig[:, None]
s = s - s.mean(axis=1, keepdims=True) # 去均值: 保留持续性, 单因子均值=μ
ret = mu[:, None] + s + beta[:, None] * M[None, :] + noisepythonφ=0.85 意味着因子的「好状态」会持续好几个月——过去 12 个月走强的因子,未来一两个月往往还行。这就是因子动量的燃料。
二、把因子当资产:因子动量组合怎么建#
逻辑和「用过去收益排序选股票」完全对称,只是排序对象是因子自身。每月:
- 算每个因子过去 12 个月累计收益,作为信号;
- 信号为正的因子纳入多头、等权持有,信号为负的剔除;
- 持有 1 个月,下月重算。
LOOK, REB = 12, 1
fm_w = np.zeros((NF, T))
fm_ret = np.zeros(T)
for t in range(LOOK, T):
sig12 = ret[:, t - LOOK:t].sum(axis=1) # 过去 12 月累计 = 信号
longs = sig12 > 0
w = longs.astype(float) / longs.sum() if longs.sum() else np.ones(NF) / NF
fm_w[:, t] = w
fm_ret[t] = float(np.dot(w, ret[:, t]))
fm_ret = fm_ret[LOOK:]
ew_ret = ret.mean(axis=0)[LOOK:] # 对照: 静态等权全部因子python这里没有任何未来函数:信号只用 t 之前的数据,持仓在 t 月内不变。阈值用「>0」而非「排行前几」,避免人为挑断点——本质是「顺因子的势」。
三、结果:因子动量把 Sharpe 翻了一倍多#
把单因子、静态等权组合、因子动量组合摆在一起:
| 标的 | 年化 | 波动 | Sharpe | 最大回撤 |
|---|---|---|---|---|
| 价值 / 动量 / 规模 / 质量 / 低波(单因子) | 3.7~9.2% | 13~21% | 0.22~0.61 | −55~−87% |
| 静态等权全部因子 | 7.0% | 7.4% | 0.94 | −21.2% |
| 因子动量组合 | 22.0% | 9.7% | 2.27 | −17.3% |
| 50% 动量 + 50% 静态 | 14.3% | 8.0% | 1.79 | −15.5% |
因子动量组合 Sharpe 2.27,几乎是无脑等权的 2.4 倍,且回撤更小。这说明「在哪个因子上多下注」这件事,本身就藏着大量未被静态配置利用的 alpha。

四、它赚的是什么钱:相关结构#
因子动量最妙的地方,是它和市场的低相关。在我们的样本里:
- 因子动量 vs 静态等权因子组合:0.72(不是 1,说明它不是简单复制底层因子,而是加了「择时」这一层);
- 因子动量 vs 共同市场:0.18(几乎中性——它赚的是因子之间的轮动,不是市场方向)。
信号本身也站得住脚:12 个月累计收益与下月因子收益的截面相关(IC)均值 0.327,且约 59.7% 的月份信号为正——也就是说,「因子过去强、未来还强一阵」是一个稳定存在的现象,不是噪声。

权重热力图更直观地展示了「轮动」:每个月被做多的因子在变,没有哪个因子被永久超配——因子动量做的是动态再平衡,而非押注单一风格。

五、真实陷阱(诚实版)#
1. 数据是合成的、且校准偏乐观:单因子 Sharpe 0.22~0.61、因子动量 2.27,都是为演示机制选的友好参数。真实因子动量(Ehsani-Linnainmaa-Reibstein 等文献)效应存在,但净 Sharpe 通常更温和,且高度依赖样本期。
2. 过拟合信号窗:本文用 12 个月回望,是「已知答案后选的」。实盘必须样本外确定回望窗,或用多个窗平均,否则会挑出看起来最漂亮、其实过拟合的那个。
3. 因子数量太少会过机动:只用 5 个因子,任何一个月「信号为正的因子」可能只有 1~2 个,集中度高、路径依赖强。真实应用应在几十个因子上做,分散单一因子的 regime 风险。
4. 它是市场状态的影子:因子动量在「因子轮动清晰」的 regime 里赚钱,在「所有因子一起失效」的危机里会同时亏(价值沉、动量破、质量也破)。它不是市场中性,只是低相关。
5. 交易成本与换手:每月调仓、且权重在 0/正之间跳变,换手不低。上面的数字没有扣交易费——实盘要把bid-ask、冲击成本、短期资本利得税算进去,Sharpe 会打折。
6. 与截面动量的混淆:因子动量(对因子收益做时序动量)和「在因子内做截面动量」是两件事。前者配置因子、后者选股,别混为一谈;混了容易把同一份溢价重复计数。
结语#
「把因子本身当成可交易的资产」,听起来只是视角的转换,但落地后收益结构完全不同:静态持有因子,是把因子当常量;因子动量,是把因子当有时好有时坏、且好坏会持续的状态变量。本文用 5 个合成因子证明,仅仅是「顺因子的势」这一层,就能把组合 Sharpe 从 0.94 拉到 2.27,且与市场几乎脱钩。真正的功课不在模型,而在样本外验证信号窗、控制换手、并在多因子维度上分散——否则你只是把「因子择时」这层新的自由度,又过拟合了一遍。一个稳妥的落地起点,是先在大类因子(价值/动量/质量/低波/规模)层面跑通因子动量,确认 IC 稳定为正后,再逐步下沉到更细的子因子,而不是一上来就对着几十个相关性混乱的因子做月度再平衡。
本文图表与全部数字均由
generate_factor_momentum_images.py用 numpy 从零计算生成(AR(1) 因子状态 + 因子动量组合 + 相关/IC 统计),非占位图。把ret换成你真实的因子收益序列,整套代码可直接复用。