动量因子分类:截面动量 vs 时间序列动量
聊到动量因子,很多新手第一反应就是「涨得好的继续买」。但实际做策略时,你会发现这里头有个关键分岔路——截面动量和时间序列动量。这两兄弟长得像,但内核完全不同。
我刚开始做量化那会儿,也傻傻分不清。直到有一次回测结果惨不忍睹,才逼着我认真琢磨它们的区别。今天咱们就把这事彻底讲透。
什么是截面动量?
截面动量,说白了就是「比谁更强」。
它的核心逻辑是:在同一时间点上,把所有资产按过去一段时间的收益率排序。买排名靠前的,卖排名靠后的。赚的是相对收益的差价。
举个例子你就明白了:
- 假设现在有5只股票:A涨了10%,B涨了5%,C跌了2%,D跌了8%,E跌了15%
- 截面动量会做多A和B,做空D和E
- 它不关心市场整体是涨是跌,只关心谁比谁强
我习惯把截面动量叫做「选美策略」。你想想看,选美比赛比的不是绝对美丑,而是相对排名。截面动量也是这个道理。
核心公式:
# 截面动量信号
def cross_sectional_momentum(returns, lookback=20):
# 计算过去20天的累计收益
past_returns = returns.rolling(lookback).mean()
# 横截面排序,做多前20%,做空后20%
rank = past_returns.rank(axis=1, pct=True)
long = rank > 0.8
short = rank < 0.2
return long.astype(int) - short.astype(int)
什么是时间序列动量?
时间序列动量,关注的是「自己跟自己比」。
它的逻辑是:单个资产过去涨了,未来还会继续涨。赚的是趋势延续的钱。
还是刚才的例子:
- 股票A过去涨了10%,时间序列动量会做多A
- 股票E过去跌了15%,时间序列动量会做空E
- 它不管其他股票表现如何,只看自己的历史
我个人觉得,时间序列动量更像「追涨杀跌」。你打开K线图,看到一只股票沿着20日均线往上走,就买入持有——这就是最朴素的时间序列动量。
避坑指南:我曾经犯过一个错误,把截面动量和时间序列动量的信号混在一起用,结果回测曲线像过山车。后来才明白,这两个信号的相关性其实很低,强行组合反而会互相抵消。
两者的核心区别
| 对比维度 | 截面动量 | 时间序列动量 |
|---|---|---|
| 比较对象 | 同一时间点,不同资产 | 同一资产,不同时间点 |
| 信号来源 | 相对强弱 | 绝对趋势 |
| 市场环境 | 震荡市也有效 | 需要明显趋势 |
| 多空结构 | 天然多空配对 | 可单边可多空 |
| 容量限制 | 受限于资产数量 | 单个资产即可 |
| 回撤特征 | 相对稳定 | 趋势反转时大回撤 |
嗯,这里要注意一个关键点:截面动量在震荡市里也能赚钱,因为它赚的是相对收益。但时间序列动量在震荡市里就惨了,频繁止损是家常便饭。
实战中的选择逻辑
我做了这么多年策略,总结出一个经验:选哪个,得看你的交易品种和市场环境。
什么时候用截面动量?
- 资产池足够大(比如全A股、美股全市场)
- 市场处于震荡或慢牛
- 你想做中性策略,对冲大盘风险
什么时候用时间序列动量?
- 交易品种少(比如只做几个期货品种)
- 市场有明显趋势(比如2020年的商品牛市)
- 你愿意承受趋势反转时的回撤
重要提醒:千万别以为截面动量就比时间序列动量高级。它们只是适用场景不同。我见过有人硬把截面动量用在只有5个品种的期货组合上,结果信号稀疏得可怜,一年都开不了几次仓。
一个实战案例
给你看个我实际跑过的例子。2022年,我在商品期货上同时测试了两种动量:
# 截面动量 vs 时间序列动量 对比回测
import pandas as pd
import numpy as np
# 假设有10个期货品种,500个交易日的数据
np.random.seed(42)
returns = pd.DataFrame(
np.random.randn(500, 10) * 0.02,
columns=['RB', 'HC', 'I', 'J', 'JM', 'ZC', 'FG', 'MA', 'TA', 'PP']
)
# 截面动量
def cs_momentum(ret, lookback=20):
cum_ret = (1 + ret).rolling(lookback).apply(np.prod) - 1
rank = cum_ret.rank(axis=1, pct=True)
return (rank > 0.7).astype(int) - (rank < 0.3).astype(int)
# 时间序列动量
def ts_momentum(ret, lookback=20):
cum_ret = (1 + ret).rolling(lookback).apply(np.prod) - 1
return np.sign(cum_ret)
# 对比结果
cs_signal = cs_momentum(returns)
ts_signal = ts_momentum(returns)
print(f"截面动量平均持仓比例: {cs_signal.abs().mean().mean():.2%}")
print(f"时间序列动量平均持仓比例: {ts_signal.abs().mean().mean():.2%}")
跑出来的结果很有意思:截面动量的平均持仓比例只有40%左右,而时间序列动量能达到60%以上。这意味着时间序列动量在市场上暴露的时间更长,风险敞口也更大。
两者能结合吗?
当然可以。我现在的策略里,就同时用了两种动量。
具体做法是:
- 先用截面动量选出排名前20%的资产
- 再对这些资产做时间序列动量,确认趋势方向
- 只有两个信号都指向做多时,才真正开仓
这样做的好处是:截面动量帮你过滤掉弱势资产,时间序列动量帮你确认入场时机。双重过滤后,信号质量明显提升。
一个小技巧:如果你做多因子模型,可以把截面动量和时间序列动量作为两个独立的因子,分别计算IC值。你会发现它们的相关性通常低于0.3,组合起来能有效分散风险。
知识体系总览
为了让你更直观地理解这两种动量的关系,我画了张图:
这张图把两种动量的关系梳理得很清楚。你看,它们从根节点分叉后,各自发展出完全不同的逻辑体系。截面动量强调「横向对比」,时间序列动量强调「纵向对比」——这就是它们最本质的区别。
好了,关于截面动量和时间序列动量的分类,我就讲这么多。记住一点:没有绝对的好坏,只有合不合适的场景。你回去看看自己的策略,用的是哪种动量?想清楚了再动手优化。