Appearance
第 8 讲:进入主线——动量轮动
本讲要点
不猜单票买卖点,而在股票池里按相对强弱定期换仓。
1. 和前 7 讲什么关系
前 7 讲用一只指数 ETF 练完了研究纪律,并亲眼看到:
规则说清 → 回测 → 有基准 → 样本内选参 → 换窗后参数过拟合
结论已经写在第 7 讲:单一标的上叠规则、调参数,风险很大。从这一讲起,进入本书主线:池内动量轮动。
对象从「一只 ETF」换成「一篮子可投资标的」;评价从「单票画线」换成「整个组合」。
2. 动量轮动 在干什么
拆成三步就够了:
- 定池子(Universe):先约定哪些票/ETF 可以买(例如一篮子行业 ETF,或筛选后的优良股)
- 算动量:看过去一段时间谁涨得相对更强(常用过去 N 日收益率)
- 轮动持有:每隔固定周期(如每月),只持有排名靠前的若干只,其余清掉或换出
这就是横截面上的「比谁更强」,不是纵向堆 MACD + RSI + KDJ。
单票叠指标 | 池内动量轮动 | |
|---|---|---|
| 问的问题 | 这只票该不该买? | 池子里此刻相对最强的是谁? |
| 持仓 | 往往盯一只 | 持有排名靠前的一组 |
| 动作 | 猜拐点 | 定期按排名再平衡 |
3. 最小可运行例子(用几只 ETF 演示)
下面用几只常见 ETF 演示「月末按近 20 日收益排序,持有最强的 2 只、等权」。
真实课程里可以把 ETF 换成你筛选后的优良股池;流程一样。
python
import numpy as np
import pandas as pd
import yfinance as yf
symbols = ["510300.SS", "510500.SS", "159915.SZ", "518880.SS"] # 沪深300 / 中证500 / 创业板 / 黄金
start, end = "2021-01-01", "2026-03-04"
download_end = (pd.Timestamp(end) + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
raw = yf.download(
symbols, start=start, end=download_end,
auto_adjust=True, progress=False,
)["Close"].loc[:end].dropna(how="all")
# 每月最后一个交易日再平衡
month_end = raw.resample("ME").last().dropna()
lookback = 20 # 用日频算动量时,先对齐到调仓日附近的收益
# 简化:用「调仓日相对上个调仓日」的月收益当动量
mom = month_end.pct_change()
top_n = 2
weights = pd.DataFrame(0.0, index=mom.index, columns=mom.columns)
for dt, row in mom.iloc[1:].iterrows(): # 第一期没有动量
ranked = row.dropna().sort_values(ascending=False)
picks = ranked.head(top_n).index
if len(picks) == 0:
continue
weights.loc[dt, picks] = 1.0 / len(picks)
# 下期收益 × 本期权重(常见的一期滞后,避免偷看当月)
next_ret = month_end.pct_change().shift(-1)
port_ret = (weights * next_ret).sum(axis=1).dropna()
equity = (1 + port_ret).cumprod()
print("调仓次数:", int((weights.sum(axis=1) > 0).sum()))
print("累计收益:", f"{equity.iloc[-1] - 1:.2%}")
print("最近一期持仓:")
print(weights.iloc[-2][weights.iloc[-2] > 0]) # -1 常因 shift 无收益,看倒数第二期完整脚本:easy-quant/code/08_momentum_rotation.py(含等权全池、沪深300买入持有对照与图)。
4. 运行结果
固定窗口 2021-01-01—2026-03-04,池子为沪深300 / 中证500 / 创业板 / 黄金 ETF:
text
窗口: 2021-01-01 → 2026-03-04
股票池: 沪深300, 中证500, 创业板, 黄金
规则: 月末按上月收益排序,等权持有 Top-2
调仓次数(有持仓的月末): 62
持仓成员变化次数: 51
计入收益的期数: 62
轮动累计收益: 63.56%
轮动年化(近似): 9.99%
轮动最大回撤: -24.14%
等权持有全池: 48.20%
沪深300买入持有: -4.72%
相对等权全池: +15.36%
各标的入选次数:
沪深300 (510300.SS): 29/62 (46.8%)
中证500 (510500.SS): 34/62 (54.8%)
创业板 (159915.SZ): 22/62 (35.5%)
黄金 (518880.SS): 39/62 (62.9%)
最近一期持仓:
沪深300: 50%
黄金: 50%| 指标 | 数值 |
|---|---|
| 调仓次数(月末再平衡) | 62 |
| 持仓成员相对上月发生变化 | 51 次 |
| 轮动累计收益 | 63.56% |
| 轮动年化(近似) | 9.99% |
| 轮动最大回撤 | −24.14% |
| 等权持有全池 | 48.20% |
| 沪深300买入持有 | −4.72% |
| 相对等权全池 | +15.36% |
| 标的 | 入选次数 | 入选占比 |
|---|---|---|
| 黄金 | 39/62 | 62.9% |
| 中证500 | 34/62 | 54.8% |
| 沪深300 | 29/62 | 46.8% |
| 创业板 | 22/62 | 35.5% |

读法:
- 调仓次数 62:约每个月末都再平衡一次(样本内有持仓的月末数)。
- 成员变化 51 次:多数月份 Top-2 名单相对上月有调整,轮动并不「躺平」。
- 本窗口内轮动累计收益高于等权全池,也明显高于单持沪深300;但这是样本内演示,池子、回看窗口与 Top-N 均可调,仍须做基准与样本外检验。
- 黄金入选最频繁,说明本段路径上贵金属动量多次排进前二——换窗后未必如此。
5. 验收时盯什么
不要只看「最终赚了多少」。至少问:
- 相对谁:相对等权持有全池、或一只宽基指数,有没有优势?
- 换手高不高:本例成员变化 51/62,轮动较勤,成本与滑点会侵蚀账面收益(后面讲执行时再细算)
- 样本外还在不在:换一段时间、换一版池子,排名逻辑是否仍成立?
前 7 讲练过的「怀疑漂亮曲线」,在这里同样适用——只是对象从均线参数,换成了池子、回看窗口、持有数量、调仓频率。
6. 本讲小结
- 主线是
动量轮动:池内比强弱,定期换仓 - 先定
Universe,再排名,再持有 Top-N - 用组合指标验收,并继续做样本外检验
→ 上一讲:过拟合 · 下一讲:股票池 Universe · 目录