trading/doc_07_etf_rotation.md

26 KiB
Raw Permalink Blame History

A 股行业 ETF 轮动策略

从零开始的量化投资实践指南(第七篇)

配套代码quant_etf_rotation_demo.py 目标读者:量化投资初学者,有 Python 基础,计划在 A 股实战 核心主题:用动量信号驱动行业 ETFExchange-Traded Fund交易所交易基金轮动构建兼顾收益与风控的量化策略


目录

  1. 为什么选 ETF 轮动?
  2. A 股 ETF 的独特优势
  3. 核心武器:动量因子
  4. 策略一:相对动量轮动
  5. 策略二:双动量策略
  6. A 股交易成本模型
  7. 回测结果深度解读
  8. 动量策略的局限性与风险
  9. 进阶优化方向
  10. 实战路径:接入真实数据
  11. 双语术语表

1. 为什么选 ETF 轮动?

1.1 初学者面临的困境

刚进入量化投资领域,你可能遇到这样的困惑:

  • 选哪只股票A 股有 5000+ 只,信息量巨大
  • 如何避免个股"雷"?财务造假、黑天鹅事件防不胜防
  • 机构有研究员团队,个人投资者如何竞争?

行业 ETF 轮动Sector ETF Rotation正是为了解决这些问题而生的策略。

1.2 轮动策略的核心逻辑

不去赌哪只股票涨
而是判断哪个"赛道"当前最强
持有最强赛道的 ETF定期切换

这背后有坚实的经济学逻辑:行业景气周期Industry Business Cycle

经济不同阶段,不同行业表现各异:

经济复苏期 → 消费、金融领跑
经济过热期 → 能源、原材料领跑  
经济衰退期 → 防御性行业(医疗、国债)抗跌
经济萧条期 → 国债、黄金是避风港

1.3 ETF 轮动 vs 个股选股 对比

维度 ETF 轮动 个股选股
难度 较低 极高
所需信息 行业趋势(公开) 个股基本面(深度研究)
黑天鹅风险 ETF 内部分散) 高(单只股票可能暴雷)
信息竞争 与机构差距较小 机构有绝对信息优势
成本 低(管理费 0.15-0.5%/年)
回测可靠性 较高(无幸存者偏差) 低(需处理退市股票)

💡 初学者建议:先用 ETF 轮动打好基础,建立量化思维框架,再逐步进阶到个股 Alpha 挖掘。


2. A 股 ETF 的独特优势

2.1 最关键的规则ETF 可以 T+0 交易

A 股个股有 T+1Trade plus 1 day限制今天买入的股票明天才能卖出。

但 ETF 是个例外:

ETF场内= T+0
  → 今天买入,今天就可以卖出
  → 策略执行更灵活
  → 急跌时可以当天止损

个股 = T+1
  → 买入后被"锁仓"一天
  → 暴跌时无法当天止损

⚠️ 注意ETF 的 T+0 指的是场内交易(通过股票账户买卖)。场外申购赎回仍然是 T+1 或 T+2。

2.2 A 股热门行业 ETF 池

以下是常见的 A 股行业/主题 ETF供参考实际投资请核实最新信息

代码 名称 跟踪指数 特点
159995 芯片 ETF 中华半导体芯片指数 高波动,含存储/AI芯片
159819 人工智能 ETF 人工智能主题指数 2019年后成立历史短
516160 新能源 ETF 中证新能源指数 涵盖光伏/风电/储能
159928 消费 ETF 中证主要消费指数 防御性,波动较低
512170 医疗 ETF 中证医疗指数 防御性,受政策影响大
512880 证券 ETF 中证全指证券公司指数 与市场 Beta 高度相关
511010 国债 ETF 上证5年期国债指数 避险资产,熊市避风港

💡 关键设计:在 ETF 池中纳入国债 ETFBond ETF作为"避险舱位"Safe Haven。当所有行业 ETF 动量均为负时,资金自动切换到国债 ETF而不是空仓等待。

2.3 A 股 ETF 的局限性

  • 历史短:多数主题 ETF 在 2018-2020 年后才成立,回测样本不足 5 年
  • 规模效应:小规模 ETF<5亿存在清盘风险流动性差
  • 跟踪误差Tracking ErrorETF 实际表现与指数有偏差
  • 同质化严重:芯片/半导体相关 ETF 众多,高度重叠

3. 核心武器:动量因子

3.1 什么是动量?

动量Momentum是量化投资中最经典的异象Anomaly之一

过去表现强的资产,在未来短期内倾向于继续表现强。

(Assets that have performed well in the past tend to continue performing well in the near future.)

这个现象最早由 Jegadeesh & Titman1993在美股系统性记录此后在全球多个市场都得到了验证。

3.2 动量为什么有效?行为金融学解释

动量之所以持续存在,背后有两个行为偏差支撑:

① 反应不足Under-reaction

好消息发布 → 投资者缓慢消化 → 价格缓慢上涨(而非一步到位)
→ 动量策略可以"搭便车"

② 追涨杀跌Herding / Trend-chasing

价格上涨 → 媒体报道 → 更多投资者买入 → 继续上涨
→ 趋势自我强化直到过度Overshooting

3.3 动量的三个维度

维度 定义 回望期
短期动量 近期强势,可能含"反转"噪音 1-3 个月
中期动量 经典动量信号,最稳定 6-12 个月
跳过期 跳过最近 1 个月(避免短期反转) 1 个月

经典公式12-1 动量)

动量得分 = 前 12 个月收益率(跳过最近 1 个月)

Momentum = Price[t-1] / Price[t-13] - 1

3.4 复合动量得分Composite Momentum Score

单一窗口的动量信号容易过拟合Overfitting。Demo 中使用三个窗口的等权平均:

# 三个窗口的动量因子
mom_12_1  = 过去12个月动量跳过1个月   # 中长期趋势
mom_6_1   = 过去6个月动量跳过1个月    # 中期趋势
mom_3_1   = 过去3个月动量跳过1个月    # 短期趋势

# 复合得分 = 三者等权平均
mom_composite = (mom_12_1 + mom_6_1 + mom_3_1) / 3

为什么要多窗口平均?

  • 减少单一参数的过拟合风险
  • 兼顾不同时间尺度的趋势
  • 提高信号的稳定性

4. 策略一:相对动量轮动

4.1 策略逻辑

相对动量Relative Momentum回答的是

"在当前所有行业 ETF 中,谁是最强的?"

每月月末执行:
1. 计算所有行业 ETF 的复合动量得分
2. 排序,选出得分最高的前 K 只Demo 中 K=3
3. 等权持有,下月月末再次评估

4.2 伪代码Pseudo Code

每月月末:
    scores = {}
    for etf in sector_etfs:
        scores[etf] = calc_momentum(etf, lookback=12, skip=1)
    
    # 选出最强的前 3 只
    top_3 = sorted(scores, by=score, descending=True)[:3]
    
    # 等权持有
    target_weights = {etf: 1/3 for etf in top_3}
    
    # 执行换仓(扣除成本)
    rebalance(current_weights, target_weights)

4.3 相对动量的优缺点

优点

  • 逻辑简单,规则清晰,不受主观判断影响
  • 自动轮换到最强赛道,捕获行业趋势
  • 历史上在多数市场有效

缺点

  • 熊市无保护:即使所有行业都在跌,也会持有"跌得少"的行业 ETF
  • 换手率较高(月度换仓约 17%),成本不可忽视
  • 趋势逆转时可能遭受较大回撤Drawdown

5. 策略二:双动量策略

5.1 Gary Antonacci 的原创思想

双动量Dual Momentum由量化基金经理 Gary Antonacci 在其 2014 年著作《双动量投资》(Dual Momentum Investing)中系统提出。

核心思想:把绝对动量和相对动量结合,既追强者,也知进退。

5.2 双动量的两道过滤

第一道绝对动量过滤Absolute Momentum Filter
  → 问题:"这只 ETF 比无风险收益(国债)表现得更好吗?"
  → 如果动量 < 0即跑输无风险说明处于下行趋势切换至国债 ETF 避险

第二道相对动量排序Relative Momentum Ranking
  → 问题:"在通过第一道过滤的 ETF 中,谁最强?"
  → 选相对最强的前 K 只

5.3 完整决策流程

月末执行双动量策略:

for each ETF in 行业ETF池:
    if 该ETF动量得分 > 0:            ← 绝对动量为正(跑赢无风险)
        候选池.add(ETF)
    else:
        跳过(趋势向下)

if len(候选池) == 0:
    持有国债ETF全部资金避险      ← 全市场下行,切换至避险
else:
    选候选池中动量得分最高的前K只    ← 相对动量选最强
    等权持有

5.4 为什么要有"绝对动量"这一关?

一个直觉性的例子:

假设某月6 只行业 ETF 的动量得分分别是:
  芯片    -15%   ← 在跌
  AI      -12%   ← 在跌
  新能源  -18%   ← 在跌
  消费    -5%    ← 跌最少(相对最强!)
  医疗    -8%    ← 在跌
  证券    -20%   ← 在跌

相对动量策略:会买消费 ETF相对最强结果仍然亏损
双动量策略:全部动量为负 → 切换至国债 ETF保住本金

绝对动量是双动量策略最核心的风控机制。

5.5 回测中的实际表现

在 Demo2019-2024 牛市主导)的结果中:

双动量切换至国债 ETF 的月数1 次(约占 2%

解读:
2019-2024 年 A 股整体处于震荡偏牛格局,
绝对动量为负的情形极少出现,
因此双动量的避险机制几乎没有触发机会。

但在 2018 年A股全面熊市或 2022 年(新能源大跌),
双动量的避险效果会非常显著。

💡 关键认识:双动量在牛市中跑输相对动量和等权(因为避险机制是"成本"),但在熊市和震荡市中保护效果极佳。评估策略要看完整市场周期,而非单一牛市区间。


6. A 股交易成本模型

6.1 为什么成本比你想象的重要?

很多初学者的回测忽略了交易成本,导致实盘效果大幅低于回测。

对于月度再平衡的 ETF 轮动策略,成本明细如下:

成本类型 方向 费率 说明
佣金Commission 买卖双向 0.03% 网络券商优惠后,通常 0.02-0.03%
印花税Stamp Duty 仅卖出 0.10% 国家税收A股特有ETF 同样收取
滑点Slippage 买卖双向 0.05% 因订单冲击导致的不利价差

单次月度再平衡的双边成本

买入成本 = 佣金 0.03% + 滑点 0.05% = 0.08%
卖出成本 = 佣金 0.03% + 印花税 0.10% + 滑点 0.05% = 0.18%
双边合计 = 0.26%

年度 12 次再平衡(假设每次换手 20%
年化成本拖拽 ≈ 12 × 0.26% × 20% ≈ 0.62%

若换手率更高(如每次 50%
年化成本拖拽 ≈ 12 × 0.26% × 50% ≈ 1.56%

⚠️ 实战建议

  • 选择低佣金券商(如华泰、富途等,佣金可低至 0.015%
  • 控制换手率(考虑季度再平衡替代月度)
  • ETF 点差通常比个股小,但也要在流动性好时交易

6.2 成本对策略的影响

Demo 回测显示,相对动量策略的总交易成本约占净值的 4.25%(跨越 6 年,平均每年约 0.7%)。

这个数字看似不大,但复利效应显著:

假设策略毛收益 18%,扣除成本后:
  年化成本 0.7% × 6年 → 累计净值差异约 5-6%
  即:回测累计 178% → 实际约 170%(差异约 8 个百分点)

7. 回测结果深度解读

7.1 三策略绩效汇总

指标 等权基准 相对动量 双动量
年化收益率 (CAGR) 18.1% 18.6% 15.9%
年化波动率 (Volatility) 17.5% 20.3% 20.4%
夏普比率 (Sharpe) 0.926 0.847 0.729
最大回撤 (Max Drawdown) -27.3% -32.2% -39.1%
卡玛比率 (Calmar) 0.663 0.578 0.407
月度胜率 (Win Rate) 52.2% 47.8% 44.9%

7.2 等权基准为何最优?——牛市的陷阱

这个结果出乎很多人的意料,但背后有清晰的逻辑:

2019-2024 年A 股整体处于"结构性牛市"Structural Bull Market

芯片/AI/新能源轮番领涨 → 行业轮动 ETF 池整体表现强劲

等权基准 = 始终全仓持有,没有错过任何一波行情

相对动量 = 追涨热门板块,但错过了"从低位起涨"的初期涨幅
          因为动量是滞后信号Lagging Indicator

双动量 = 额外的避险机制在牛市里是"白交保险费"

这说明了一个重要的回测教训:

在单一市场环境(纯牛市)中评估策略,结论极不可靠。 真正有价值的策略需要跨越完整市场周期(牛市 + 熊市 + 震荡市)来评估。

7.3 如何正确评价双动量策略?

尽管在 Demo 的牛市数据中表现靠后,双动量策略在以下情形下优势明显:

市场环境 等权基准 相对动量 双动量
牛市(全面上涨) 最优 次优 最差
熊市(全面下跌) 随市大跌 跌得少一点 切债避险
震荡市(分化) 捕获强势 回避弱势

结论:双动量的价值在于"不对称性"——牛市少赚一点,熊市保住本金。

对于注重本金安全的初学者,这个特性极为宝贵。

7.4 持仓集中度分析

从持仓热力图可以看到一个有趣的现象:

芯片 ETF 被选中频率最高82%的月份)
医疗 ETF 排第二70%

这反映了 2019-2024 年 A 股的主要趋势:
半导体/科技 + 医疗健康是这一周期的主赛道

⚠️ 重要警示:历史持仓集中度≠未来持仓集中度。 不能因为芯片 ETF 过去 5 年强,就预设它未来也会被持续选中。


8. 动量策略的局限性与风险

8.1 动量崩溃Momentum Crash

动量策略最大的单一风险是动量崩溃Momentum Crash

发生条件:市场快速从下跌趋势逆转为强劲上涨
          (通常发生在熊市见底后的"V形反弹"

典型案例2009年3月全球股市V形反弹
          动量策略空仓"弱势股"、持有"强势股"
          结果:弱势股(低价小盘股)反而率先暴涨
                强势股反而横盘,动量策略大幅跑输

A股案例2020年3月疫情底部反弹、2022年底政策转向
         持有前期强势行业反而错过了反弹

应对方法

  1. 在动量信号基础上叠加估值过滤器(避开极度高估行业)
  2. 设置止损机制(如最大回撤超过 15% 时切换至国债)
  3. 降低单一赛道集中度(强制分散,如最多持有行业不超过 2 只)

8.2 过拟合风险Overfitting Risk

回测中 Demo 选择了 12-1 月、6-1 月、3-1 月三个窗口。如果不断调整这些参数直到回测结果最好,就会陷入过拟合

过拟合的症状:
  回测夏普比率 > 2.0
  换不同参数结果差异极大
  真实数据(实盘)效果远低于回测

防止方法:
  1. 固定参数(如永远用 12-1 月,不要调整)
  2. 预留测试集(后 30% 的数据不参与参数选择)
  3. 参数敏感性测试(改变 ±2 个月,结果不应剧烈变化)

8.3 主题 ETF 历史过短

这是 A 股 ETF 轮动策略的致命弱点之一:

A 股主要主题 ETF 成立时间:
  芯片 ETF1599952019年5月
  人工智能 ETF1598192019年10月
  新能源 ETF5161602021年6月
  存储芯片 ETF5628502022年12月

问题:
  → 最长历史仅 5-6 年(不含完整牛熊周期)
  → 成立于牛市阶段的 ETF回测结果天然偏好
  → 历史太短,难以区分"策略有效"还是"恰好运气好"

💡 建议:至少需要 10 年以上历史含一次完整熊市才能对策略可靠性有初步信心。A 股历史较长的 ETF如沪深 300 ETF 5103002012年成立更适合做长期回测验证。

8.4 政策风险与流动性风险

A 股特有风险ETF 也不能免疫:

风险类型 描述 影响
政策突变 双减政策(教育)、反垄断(互联网) 行业 ETF 单日 -10%
涨跌停传导 ETF 内部成分股大面积涨停/跌停 ETF 价格与净值偏离(折溢价)
清盘风险 规模低于 5000 万的小 ETF 可能被清盘 被迫在低点卖出
流动性枯竭 小众主题 ETF 某些时段成交量极低 买卖价差扩大,滑点增加

9. 进阶优化方向

9.1 叠加估值过滤器

纯动量有时会在行业极度高估时继续追入。加入估值信号可以降低这种风险:

# 概念示例:动量 + 估值复合信号
def composite_score(etf, date):
    mom_score = calc_momentum(etf, date)          # 动量得分
    
    # 估值得分PE 历史分位数越低,分数越高
    pe_percentile = calc_pe_percentile(etf, date)  # 0~11表示历史最贵
    val_score = 1 - pe_percentile                  # 越便宜分数越高
    
    # 复合信号:动量 70% + 估值 30%
    return 0.7 * normalize(mom_score) + 0.3 * val_score

实战 ETF 估值参考

  • 中证指数官网提供各指数 PE/PB 历史数据
  • 恒生前海基金等定期发布行业估值分位数报告
  • AKShare 可获取宽基指数 PE 数据

9.2 波动率调仓Volatility Scaling

在高波动时期降低持仓比例,类似"自动降速"机制:

def vol_scaled_weight(base_weight, current_vol, target_vol=0.15):
    """
    目标波动率Target Volatility仓位调整
    若当前波动率过高,则降低持仓比例
    
    例:目标波动 15%,当前波动 30%
        → 实际持仓 = 50%(另 50% 转为国债 ETF
    """
    scale = min(target_vol / current_vol, 1.0)
    return base_weight * scale

9.3 宏观择时信号

利用宏观指标提前判断牛熊切换,辅助双动量的避险决策:

信号类型        数据来源             使用逻辑
-----------------------------------------------------------------
利率曲线        国债收益率           10Y-2Y 倒挂 → 熊市预警
信用利差        企业债 vs 国债       利差扩大 → 风险偏好下降
PMI 趋势        国家统计局           PMI < 50 且下行 → 降低权益仓位
市场情绪        北向资金流入/流出    持续流出 → 外资撤退信号

9.4 季度再平衡降低成本

将月度再平衡改为季度再平衡,换手率降低约 2/3成本拖拽从 ~1.5% 降至 ~0.5%

月度再平衡:年化成本约 1.0-2.5%(视换手率)
季度再平衡:年化成本约 0.3-0.8%
半年度再平衡:年化成本约 0.2-0.4%

代价:信号响应更慢,可能错过行情初期

10. 实战路径:接入真实数据

10.1 环境准备

# 安装真实数据接口
pip install akshare        # 免费,数据较全
pip install tushare        # 部分接口需积分/付费

10.2 用 AKShare 获取 ETF 真实数据

import akshare as ak
import pandas as pd

def get_etf_history(etf_code: str, 
                    start_date: str = "20190101",
                    end_date:   str = "20241231") -> pd.DataFrame:
    """
    获取 A 股 ETF 历史日线数据
    Get A-Share ETF historical daily data
    
    参数:
      etf_code  : ETF 代码,如 "159995"(不含交易所后缀)
      start_date: 开始日期,格式 "YYYYMMDD"
      end_date  : 结束日期,格式 "YYYYMMDD"
    
    返回:包含 date, close, volume 等列的 DataFrame
    """
    df = ak.fund_etf_hist_em(
        symbol=etf_code,
        period="daily",
        start_date=start_date,
        end_date=end_date,
        adjust="qfq"          # 前复权Forward Adjusted Price处理分红/拆股
    )
    df = df.rename(columns={"日期": "date", "收盘": "close", 
                             "成交量": "volume", "开盘": "open"})
    df["date"] = pd.to_datetime(df["date"])
    df = df.set_index("date").sort_index()
    return df[["open", "close", "volume"]]


# 示例:批量获取 ETF 池数据
ETF_CODES = ["159995", "159819", "516160", "159928", "512170", "512880", "511010"]

etf_data = {}
for code in ETF_CODES:
    try:
        etf_data[code] = get_etf_history(code)
        print(f"✅ {code}: {len(etf_data[code])} 条记录")
    except Exception as e:
        print(f"❌ {code}: 获取失败 - {e}")

# 合并为价格矩阵
price_matrix = pd.DataFrame({
    code: df["close"] 
    for code, df in etf_data.items()
})

10.3 关键注意事项:前复权 vs 后复权

复权方式 说明 用途
前复权Forward Adjusted 以当前价格为基准向历史调整 回测首选:历史价格连续,收益率计算准确
后复权Backward Adjusted 以上市初始价为基准向未来调整 长期持有者参考
不复权 原始价格,含分红跳空 不适合回测,会产生虚假信号

⚠️ 前复权的陷阱:前复权价格会随时间变化(每次分红后历史价格都重新调整)。在回测中,必须在同一时点下载数据,否则历史价格不一致。

10.4 从合成数据切换到真实数据的步骤

# 替换 Demo 中的 §0 数据生成部分
# 将合成 price_df 替换为真实数据:

# 原来(合成数据):
# price_df = (1 + returns_df).cumprod()

# 替换为(真实数据):
price_df = price_matrix  # 已经是收盘价,无需转换

# 注意:真实数据可能有以下问题需要处理:
# 1. 缺失值(节假日、停牌)→ 前向填充 ffill()
# 2. 各 ETF 成立时间不同 → 统一以最晚成立的 ETF 作为回测起点
# 3. 价格单位不统一 → 统一转为净值形式(除以第一个有效价格)

price_df = price_df.ffill()                   # 填充缺失值
start_common = price_df.dropna().index[0]     # 统一起始日期
price_df = price_df.loc[start_common:]        # 裁剪至共同起始日
price_df = price_df / price_df.iloc[0]        # 标准化为净值形式从1开始

11. 双语术语表

中文术语 English Term 简要说明
行业 ETF 轮动 Sector ETF Rotation 在不同行业 ETF 间周期性切换
动量因子 Momentum Factor 过去表现预测未来表现的信号
相对动量 Relative Momentum 横截面比较,选相对最强
绝对动量 Absolute Momentum 纵向比较,判断趋势正负
双动量 Dual Momentum 结合绝对和相对动量的策略
避险资产 Safe-Haven Asset 市场下跌时表现抗跌的资产
再平衡 Rebalancing 定期调整组合权重至目标配置
换手率 Turnover Rate 组合中被替换的资产比例
印花税 Stamp Duty A 股卖出时征收的税0.1%
滑点 Slippage 订单执行价与预期价的差异
复权 Price Adjustment 对历史价格进行分红/拆股调整
前复权 Forward Adjusted 以当前价为基准调整历史价格
最大回撤 Maximum Drawdown 从历史最高点的最大跌幅
夏普比率 Sharpe Ratio 单位风险所获得的超额收益
卡玛比率 Calmar Ratio 年化收益 / 最大回撤
行业景气周期 Industry Business Cycle 行业随经济周期的景气变化
动量崩溃 Momentum Crash 趋势逆转时动量策略的大幅亏损
过拟合 Overfitting 策略参数过度适应历史数据
跟踪误差 Tracking Error ETF 实际表现与指数的偏差
清盘风险 Liquidation Risk ETF 规模过小被强制清算的风险
估值分位数 Valuation Percentile 当前估值在历史中所处的位置
波动率调仓 Volatility Scaling 根据波动率动态调整持仓比例
目标波动率 Target Volatility 组合设定的波动率上限目标
信用利差 Credit Spread 企业债与国债的收益率差异
T+0 T+0 Trading 当天买入当天可以卖出
T+1 T+1 Trading 当天买入次日才能卖出(个股规则)
折溢价 Premium/Discount ETF 市价与净值的偏离程度

附录:策略参数速查

参数 Demo 默认值 可调范围 影响
动量回望期 12-1, 6-1, 3-1 月 3-24 月 更长=更稳定但更滞后
持仓 ETF 数量 (K) 3 1-5 更少=更集中,更多=更分散
再平衡频率 月度 周/月/季 更频繁=响应快但成本高
佣金率 0.03% 0.015-0.05% 影响成本拖拽
印花税 0.10% 固定 不可更改

下一步建议

  1. 用 AKShare 接入真实 A 股 ETF 数据,替换合成数据后重新回测
  2. 在动量信号基础上叠加估值过滤器(参考第 9.1 节)
  3. 参考《双动量投资》Gary Antonacci原著深入理解策略理论基础
  4. 结合 doc_06_astock_practice_guide.md 的风险管理框架,制定完整的实战纪律

本文档配套代码:quant_etf_rotation_demo.py 系列文档索引doc_01数据准备→ doc_02策略回测→ doc_03事件驱动→ doc_04Alpha因子→ doc_05组合优化→ doc_06A股实战指南doc_07ETF轮动