「买涨不买跌」是散户的坏习惯,但在另一个尺度上,它却是十年不败的印钞机:商品交易顾问(CTA)基金靠的就是趋势跟踪(Trend Following)——在股票、债券、商品、外汇之间,哪里在涨就做多、哪里在跌就做空,用纯粹的「跨资产时序动量」把宏观的牛熊拐点变成收益。
它最迷人的地方在于「危机 Alpha」:2008、2020 这种别人血亏的月份,趋势跟踪往往反而赚钱。本文用 8 个跨资产类别、30 年(360 个月)的合成收益面板,从零实现一套标准的 CTA 时序动量(TSMOM)策略,把「为什么动量能赚钱、又在哪里会翻车」跑成看得见的数据。
在海外,CTA 早已是一个独立的百亿级策略类别:商品趋势、股指突破、国债与外汇的 carry,都被装进同一个「多空、跨资产、系统化」的框架。它不预测宏观,只跟随已经发生的趋势——这恰恰是它能穿越牛熊的原因:趋势还在,它就还在;趋势反转,它认错离场。

一、时序动量:不看方向,只看「过去是不是还在涨」#
横截面动量问的是「谁比谁强」(多空中性、做多赢家做空输家);**时序动量(TSMOM)**问的是「这个资产自己是不是还在涨」——涨就做多、跌就做空,天然是方向性的。
最经典的写法是 Moskowitz、Ooi 与 Pedersen(2012)的 12 个月信号:
import numpy as np, pandas as pd
LOOK = 12
pos = pd.DataFrame(index=R.index, columns=R.columns, dtype=float)
for j, n in enumerate(R.columns):
ret = R[n].values
for t in range(LOOK, len(ret)):
mom = np.prod(1.0 + ret[t-LOOK:t]) - 1.0 # 截至 t-1 的 12 个月累计收益
pos.iloc[t, j] = np.sign(mom) # 涨→+1(做多),跌→-1(做空)
pos = pos.fillna(0.0)python关键的无前视纪律:第 t 个月的仓位,用的是「截至 t-1 收盘」的 12 个月收益来计算,然后去赚第 t 个月的收益。信号在 t-1 收盘确认、在 t 开盘(或月内)执行——这是回测里最容易被偷偷违反、却决定策略真伪的一条线。
为什么市场会允许这种「追涨杀跌」长期赚钱?学界给过两类解释:其一是行为金融——投资者对消息反应不足、趋势形成后才缓慢追价,留下可捕捉的惯性;其二是风险补偿——趋势跟随者在市场暴跌时做空,本质是卖出了一份「尾部保险」,平时拿小钱、危机赚大钱,要求的正是这份负 gamma 的补偿。两类解释并不矛盾,共同说明:趋势不是免费的午餐,而是对你承担「在众人恐慌时反向操作」这种不舒服任务的奖赏。
二、组合层:逆波动权重 + 波动率目标#
单资产满仓多空太野蛮。真实的 CTA 用两步把风险压平:
- 逆波动权重:每个资产按其历史波动的倒数分配权重,
w_i = (1/σ_i) / Σ(1/σ_i),波动高的资产自动少配——近似等风险贡献。 - 波动率目标(Vol Target):用一个滚动窗口估计组合实际波动,乘上一个杠杆系数把总波动钉在例如 10%/年,危机时波动飙升、杠杆自动收缩。
vol_win = 12
asset_vol = R.rolling(vol_win).std().bfill()
inv_vol_w = (1.0 / asset_vol).div((1.0 / asset_vol).sum(axis=1), axis=0)
strat_gross = (pos.shift(1) * R * inv_vol_w.shift(1)).sum(axis=1) # 信号 t-1 → 收益 t
port_vol = strat_gross.rolling(vol_win).std() * np.sqrt(12)
leverage = (0.10 / port_vol).clip(upper=2.0).fillna(1.0)
cta_ret = (strat_gross * leverage).fillna(0.0)python我们把同一套 8 资产做「等权买入持有」(只多、同样波动目标到 10%)当作基准,公平对比。
二·五、信号的几种变体:不止 12 个月#
12 个月符号信号只是教科书起点。实务 CTA 常用三种扩展来平滑表现:
- 通道突破(Donchian):不用「过去收益符号」,而用「价格是否突破 N 日最高/最低」做信号,本质等价但对单日跳变更钝感。
- 多周期叠加:同时跑 1 个月、3 个月、12 个月三个尺度,等权合成仓位,避免单一窗口在参数边缘「开关翻转」。
- 波动调整动量:用
mom / vol而非裸mom定方向强度,让信号自带风险刻度,弱趋势不会被误放大。
本文为演示清晰只用了单一 12 个月符号信号;真实 CTA 多用多周期组合来同时压低换手与样本外衰减。
三、回测结果:长期跑赢,且回撤浅得多#

在本面板里(含人为植入的趋势项与危机期),结果很有代表性:
| 指标 | CTA 时序动量 | 等权买入持有 |
|---|---|---|
| 年化收益 | 6.85% | 2.59% |
| 夏普比率 | 0.69 | 0.28 |
| 最大回撤 | −22.5% | −75.1% |
CTA 收益不如买入持有在「单边牛市」里风光,但回撤只有基准的三分之一——靠的是多空双向:股票崩的时候它早就在做空,债券和黄金的避险趋势又给它加了仓。

四、危机 Alpha:别人崩盘,它在收租#
这是趋势跟踪存在的根本理由。把「全球风险因子处于最低 8% 分位」的月份标为危机月,对比两类策略在这些月份的平均表现:

- CTA 危机月平均:+0.21%
- 买入持有危机月平均:−5.52%
29 个危机月里,CTA 用「做空风险资产 + 做多避险资产」的组合,把别人最痛的时段变成了自己的稳定来源。这也是为什么在机构组合里,CTA 常被当作「股票下跌时的保险」——它和股票的相关性在危机时显著转负。

五、六大真实陷阱#
能跑出漂亮回测,不等于能赚钱。CTA 时序动量至少有六个坑:
- 前视偏差:信号必须用
t-1及之前的数据,绝不能用t当月收益定t的仓位。一旦泄漏,夏普会被凭空抬高一截。 - 横截面 vs 时序混淆:TSMOM 是方向性的(可能整体净多或净空),不能和「多空中性」的横截面动量直接比收益——两者赚的钱来源不同。
- 成本吞噬:每月换仓、且空头要融券,单边成本按 10–20bps 计。趋势弱、换手高时,成本能吃掉一半以上的毛收益。真实回测必须扣成本。
- 拥挤与衰减:TSMOM 是公开、拥挤的策略。越来越多资金做同一件事,信号被提前交易,样本外衰减明显——近十几年它的夏普比 80–90 年代低了一大截。
- 震荡市钝刀割肉:没有趋势的「来回打脸」市况(如窄幅盘整)会让它反复止损,这是趋势跟踪最大的敌人,往往连亏数月。
- 参数与样本依赖:12 个月不是圣杯。把回看窗从 6 调到 18 个月,收益结构可能大变;且结论高度依赖你「喂」的那段历史里有没有足够的趋势与危机。
六、结语#
CTA 趋势跟踪不是魔法,它赚的是三笔钱:趋势持续的钱、多空双向分散的钱、以及危机时与股票负相关的保险钱。它的代价是震荡市里的磨损和样本外衰减。对量化研究者而言,更有价值的不是「跟一个 12 个月信号」,而是把它当作组合里一个低相关、危机对冲的模块——和前面聊过的波动率目标、风险平价、尾部对冲放在一起,才真正发挥「危机 Alpha」的威力。
实盘落地清单(给研究者的提醒):
- 信号确认在
t-1收盘、成交在t开盘,中间必须留出跳空与滑点成本; - 空头端的融券可得性与合约展期(roll)收益要单独建模,不能当免费;
- 用 2008、2011、2015、2018、2020 几个危机段做「危机 Alpha 是否还在」的压力测试;
- 把 CTA 当作组合里的低相关模块,而非单独押注的方向。
注:本文收益面板为含趋势项与危机结构的合成数据,用于演示方法论;真实 CTA 需接入可交易流动性、融券成本与滚动合约展期(roll)收益,且应做样本外与 walk-forward 验证。