动量是那种听起来最不需要解释的策略。涨得好的继续买,涨得差的不碰。书上有,论文有,各种回测截图也有。

把它放在三年的美股日频样本上跑完 12 组参数,结论是:动量既不能证明跑赢基准,也不能证明跑输基准。统计上两边都不成立。

这篇把过程里几个真正有信息量的地方摊开讲:成本吃掉了多少,一个我自己下过的错误判断,以及唯一显著的那组参数为什么恰恰最不能信。

跑的是什么

动量分的定义是:把收盘价取对数,对时间做线性回归,取年化斜率乘上 R²。

score=slopeannualized(lnPtt)×R2\text{score} = \text{slope}_{\text{annualized}}\big(\ln P_t \sim t\big) \times R^2

乘 R² 这一步是关键。斜率衡量涨得多快,R² 衡量涨得多稳。一只票三个月翻倍但全靠某一天跳空,R² 很低,分数会被压下去。这个定义比单纯的「过去 N 天涨幅」挑剔。

排完序取前 N 只,权重用 20 日收益标准差的倒数,波动越大给的钱越少。另外两个开关:

  • 动量阈值:分数低于 0.40 的不买,哪怕它排进了前 N。
  • 趋势过滤:全样本等权指数跌破 200 日均线时不开新仓,已经在榜内的老仓位保留。

样本是三年美股日频、516 只标的、784 个交易日。成本设三档:0 bps 看毛信号,10 bps 是回测里最常见的偷懒设定,46 bps 是日频美股策略上实际打出来的单边全成本。基准是全样本等权、每日再平衡。

参数网格在动手之前全部写死,跑完全部报告,一格不删。这条纪律是整个实验里唯一不打折的东西,后面会解释为什么。

基线:成本吃掉近 6 个百分点

书上的原始设定是 125 日窗口、Top 30、阈值 0.40、月频调仓、趋势过滤开。

口径年化收益Sharpe最大回撤
0 bps(毛)17.95%0.84-26.43%
46 bps12.01%0.61-26.85%
等权基准15.17%1.04-17.22%

毛收益 17.95% 看着是赢的。扣掉真实摩擦剩 12.01%,比什么都不做的等权组合低 3 个百分点,而且回撤深了 9 个百分点,Sharpe 只有基准的六折。

近 6 个百分点的年化被摩擦吃掉了。这个缺口大到没法用「再优化一下参数」找补。

一个我下错的判断

动手之前我算过一笔账:既然成本正比于换手,那把调仓频率从周频降到月频,换手率大概能从 60% 掉到 30% 左右,成本拖累压到 3 个百分点,净收益就能反超基准。这个判断我当时写下来了,很有把握。

跑出来的单边换手率是这样的:

调仓间隔单边换手率46 bps 净年化
周频17.7%11.2%
双周29.5%15.9%
月频48.5%12.7%
双月68.6%18.6%

月频的实际换手是 48.5%,比我估的 30% 高出六成。成本没有按预期降下来,净年化 12.7% 依然跑输基准。

更打脸的是方向。我默认「调仓越稀疏,换手越低」,实际完全相反:间隔越长,单次换手越高。周频 17.7%,双月 68.6%。理由想通了很朴素——两次调仓之间隔得越久,排名重新洗过的程度越大,到了调仓日要动的仓位就越多。一年下来的总换手也许差不多,单次的冲击完全是另一回事。

这一条比整个实验的结论更值钱:成本要用订单里数出来的换手率去估,别拿调仓次数推算。我用后者推了一次,方向和幅度全错。

顺带一提,46 bps 那一列的净年化在四个间隔上不是单调的。11.2% → 15.9% → 12.7% → 18.6%,上下跳。这种非单调本身就是在提示:这些差异更可能来自样本噪音,频率在这里未必起了作用。

唯一显著的那一格,恰好最不能信

窗口 × 阈值的网格一共 12 格。三个窗口(63 / 125 / 250 日)乘四档阈值。判断标准用 Wilcoxon 符号秩检验,检验策略和基准的日收益差是不是显著异于零。选它是因为它不要求收益服从正态分布,而股票日收益从来不正态。

12 格里只有一格 p < 0.05:250 日窗口,超额 +10 个百分点,Sharpe 1.08。其余 11 格的 p 值全部远离 0.05,包括 63 日和 125 日窗口的每一档阈值。

如果只报这一格,这篇文章可以写成《我找到了跑赢基准 10 个百分点的动量参数》。但这一格恰恰是最可疑的:

250 日窗口意味着回测起点要往后推将近一年热身,实际统计区间被压缩到 2024 年下半年往后。样本最短,同时标的池又是当前成分股——那 516 只都是活到今天的公司。样本越短、越靠近现在,「活到今天」这个筛选条件的偏乐观程度就越强。过拟合和幸存者偏差在这一格上叠加得最狠。

12 格里挑一格出来讲,本身就有大约 46% 的概率至少撞上一个假阳性(10.95121 - 0.95^{12})。所以这一格我不采纳。

阈值那个开关顺便也可以判死刑了:0.40 的阈值只在 7.1% 的调仓日真正触发过,合格标的数量的中位数是 30 只——刚好等于 Top 30 的容量。这个参数在这段样本里基本没起作用。

持仓数是明摆着的风险换收益

这一组的结果比前面干净:

持仓数46 bps 年化年化波动最大回撤
10 只33.3%34.7%-31.3%
20~30 只12% 上下-26.8% 上下
50 只9.7%

10 只的 33.3% 很扎眼,代价是 34.7% 的年化波动和 -31.3% 的回撤。10 只票的组合,任何一只出事都是 10% 的仓位。而且基线配置的收益偏度是 -0.28,缩到 10 只之后单名风险还会更集中——赚的时候一点点赚,亏的时候一次亏掉一大块。

50 只这头,分散是够了,换手带来的成本也跟着摊到更多笔交易上,年化 9.7% 已经明显低于基准。

20 到 30 是我认的区间。这不算什么发现,只是把「分散度—成本—单名风险」这个三角关系用数字量了一遍。

有两个开关根本没测出来

趋势过滤。开和关的结果差别小到看不出方向,最大回撤两边都停在 -26.8%。原因很直接:样本区间从 2024 年 3 月起基本是单边上行,等权指数就没怎么跌破过 200 日均线,这个开关几乎没有触发过。

动量阈值。同理,7.1% 的触发率,等于没测。

这两个都是下行保护机制。样本里没有像样的下跌周期,它们既不能记功也不能证伪。

这一点值得单独拎出来说,因为它适用于所有回测:任何只覆盖上涨市的样本,谈止损、过滤、风控开关都是空的。这些机制的价值只在你没经历过的那段行情里体现。看到一份回测宣称「加了趋势过滤,回撤从 -30% 降到 -15%」,第一件事是去查样本里有没有真正的下跌周期,而不是记下这个参数。

有一个数据陷阱会悄悄改掉结论

这个坑值得所有自己拉行情的人查一遍。

多数行情接口返回的复权价,是以请求时刻为基准算出来的。如果你的数据是每天增量抓一行存下来,那么每一行的复权基准都停在它被抓下来的那一天。跑一年下来,同一张表里不同日期的价格用的是不同的复权基准。

这个偏差有多大?把一只大盘股整段重拉一次,和旧数据逐日比对,同一天收盘价的最大相对偏差能到 0.87%。

0.87% 听着不多。放在一个净年化十几个百分点、单边成本 0.46% 的策略里,它足以改变结论——它比你辛苦优化参数拿到的差异还大。

处理办法是定期整段重拉,别只补缺失区间。增量更新省下来的那点带宽,换来的是一份没法复现的历史。

顺带一个相关的样本量问题:125 日窗口热身之后,一份 282 个交易日的数据只剩 81 天可用于统计。81 天画出来的净值曲线可以很好看,统计上什么都说明不了。先问样本够不够,再问参数好不好。

我现在怎么看动量

它在这个池子上没有统计上站得住的超额。绝大多数网格的 p 值远高于 0.05,唯一显著的那格来自最短样本,不采纳。说它有效或者说它失效,都超出了这份数据能支撑的范围。

摩擦是主要矛盾,参数不是。毛收益 17.95% 和净收益 12.01% 之间那近 6 个百分点,比任何一组参数调整能带来的差异都大。在执行成本没有量清楚之前,调参数是浪费时间。

它的位置是低相关配置,不是基准替代品。等权基准在这段样本里 Sharpe 1.04,动量净值 Sharpe 0.61,拿它去替代基准没道理。它的价值在于和其他因子的相关性够低,作为组合里的一路来用。

比结论更值得抄的是流程

如果这篇只留一件事,我希望是下面这个顺序。这跟动量本身没关系,任何策略都适用。

  1. 动手前把全部参数网格写死。事后加格子和事后删格子,性质一样。
  2. 跑完全部格子,全部报告,包括难看的。挑格子讲故事是参数搜索,敏感性分析不是这么做的。
  3. 用统计检验,别比单点收益。Wilcoxon 这类非参数检验对分布不挑食,比对比两个年化数字可靠得多。
  4. 成本用订单里数出来的换手率算,别用调仓次数推。
  5. 先问样本够不够,再问参数好不好。81 天和 784 天是两个世界。
  6. 数据口径先对齐。复权基准这种东西不查就永远不知道自己错在哪。

整个网格跑完,得到的是一个「没有结论」的结论。这在研究里很常见,只是很少有人愿意把它发出来——大家更愿意发那唯一显著的一格。

愿意承认没有结论,下一轮才知道该补什么。