ETF 动量轮动:一个可复现的研究框架
动量轮动并不缺少策略公式,真正稀缺的是一套经得起检查的研究过程。本文先定义研究框架,不展示未经验证的收益曲线,也不把参数选择包装成确定性结论。
建立一个可以被复现、质疑和改进的研究蓝图。后续加入真实数据时,每一步都应保留输入、输出与假设记录。
1. 先把研究问题写清楚
一个可检验的问题可以写成:
在预先确定的 ETF 候选池中,使用只依赖当时可获得信息的中期动量信号进行定期轮动,在计入合理交易成本后,能否改善组合的风险调整后表现?
这句话限定了五件事:候选池、信息时点、信号期限、调仓规则和评价标准。缺少其中任何一项,回测都很容易在事后被重新解释。
2. 固定候选池与数据口径
候选池应在回测前确定,并记录以下信息:
| 项目 | 需要记录的内容 |
|---|---|
| 标的范围 | ETF 代码、跟踪指数和资产类别 |
| 纳入时间 | 基于当时可交易状态,而不是今天仍存续的名单 |
| 价格口径 | 复权方式、分红处理和数据时区 |
| 可交易性 | 停牌、涨跌停、最小成交单位和流动性过滤 |
| 现金代理 | 现金收益率或短久期工具的处理方式 |
如果只使用今天仍然存在的 ETF 回看历史,会引入幸存者偏差。对较早时期不存在的产品,也不能用后来才发布的数据进行替代交易。
3. 定义动量信号
一个常见的中期动量定义是跳过最近一个月的十二个月收益:
\[ M_{i,t}^{12-1} = \frac{P_{i,t-1}}{P_{i,t-12}} - 1 \]
其中 \(P_{i,t-k}\) 表示在时间 \(t-k\) 已经可以观察到的复权价格。跳过最近一个月,是为了把中期趋势与短期反转效应分开讨论,而不是保证策略一定更好。
实现时必须明确:
- 月末信号在何时计算;
- 下一个可交易时点是什么;
- 缺失价格如何处理;
- 同分标的如何排序;
- 是否设置绝对动量门槛。
4. 从信号到组合
最简单的组合规则可以作为基线:
- 每月最后一个交易日收盘后计算信号;
- 对候选 ETF 按动量从高到低排序;
- 在下一交易日买入排名靠前的 \(N\) 个标的;
- 采用等权配置;
- 未通过绝对动量门槛的部分持有现金代理;
- 持有至下一次调仓。
伪代码如下:
signal = price.shift(1) / price.shift(12) - 1
ranked = signal.loc[rebalance_date].dropna().sort_values(ascending=False)
selected = ranked.head(n_assets)
weights = equal_weight(selected)
weights = apply_absolute_momentum_filter(weights, ranked)这里的 shift(1) 是为了表达信号与交易之间的时间隔离。实际实现仍需结合日历和成交假设,验证没有使用未来信息。
5. 把成本与摩擦放进基线
轮动策略对成本敏感。至少需要同时报告:
\[ \text{净收益}_t = \text{毛收益}_t - \text{换手率}_t \times \text{单边成本} - \text{其他摩擦}_t \]
成本假设不应只有一个数字。更稳健的做法是给出低、中、高三种情景,并观察策略结论是否发生方向性变化。
6. 样本外验证优先于参数优化
建议把研究分成三个阶段:
| 阶段 | 用途 | 允许做什么 |
|---|---|---|
| 训练期 | 建立假设 | 选择少量有经济含义的规则 |
| 验证期 | 比较方案 | 淘汰明显不稳健的设计 |
| 测试期 | 最终评估 | 不再修改参数,只做一次检验 |
滚动或扩展窗口比一次性切分更接近真实使用场景。参数敏感性图也通常比“最佳参数”更有信息量:如果相邻参数的结果完全不同,策略可能依赖偶然噪声。
7. 最低限度的结果报告
后续接入真实数据后,报告不应只有累计收益。最低限度包括:
- 年化收益、年化波动与最大回撤;
- 换手率、交易次数与成本敏感性;
- 相对基准的超额收益和跟踪误差;
- 不同市场阶段的表现;
- 参数邻域与候选池变化的稳定性;
- 数据、代码和运行环境版本。
8. 复现清单
在发布结果前逐项确认:
结语
一个可信的量化研究,不是从漂亮的曲线开始,而是从可证伪的问题和严格的时间顺序开始。下一步应接入明确授权的数据源,完成数据质量检查,再按照本文框架生成第一版基线结果。