中国版ETF动量轮动策略构建与改进实践
作者:梅琳marlin2026.11.07 01:31浏览量:0简介:本文以ETF为标的,系统拆解在中国市场构建动量轮动策略的完整流程,包括候选池设计、数据获取、强弱排序、信号生成、回测统计与改进思路。文章提供可执行的策略框架与源码示例,帮助读者理解如何通过量化方式捕捉资产、行业与风格轮动收益,并客观讨论策略优势、局限与改进方向。
中国版ETF动量轮动策略构建与改进实践
一、策略背景与研究动机
在投资实践中,股票、债券、商品等市场长期存在轮动效应。所谓轮动,可以理解为不同资产或不同细分品种在不同阶段表现出显著强弱差异。常见轮动类型大致可分为三类。
第一类是资产轮动,即大类资产之间的轮动。不同宏观环境下,配置不同类别资产可能获得更优收益。例如衰退期通常利好债券,复苏期股票表现更强,过热期大宗商品更受关注,滞涨期现金或短久期资产相对占优。股债轮动也属于这一范畴。
第二类是行业轮动,又称板块轮动。不同行业在同一时期表现差异明显,部分行业处于上升阶段,另一些行业则相对低迷。通过阶段性配置强势行业,可能获得超越市场基准的收益。
第三类是风格轮动。与行业轮动类似,只是强弱切换发生在投资风格层面,例如大小盘风格、成长与价值风格之间的轮动。
本文聚焦于ETF层面的动量轮动策略。选择ETF而非个股,主要考虑到以下几点:一是ETF代表一篮子股票,天然降低单一个股风险;二是ETF交易成本相对可控,流动性整体较好;三是ETF覆盖资产类别较多,便于在资产、行业、风格之间统一执行轮动逻辑。
需要说明的是,任何量化策略都不能保证未来收益。本文的目的,是提供一套相对完整的策略构建与研究框架,帮助读者理解动量轮动从数据到回测的实现路径。文中涉及的收益表现、参数设置仅用于说明方法,不构成任何投资建议。
二、初始轮动策略构建
1. 候选池确定
构建轮动策略的第一步,是明确在哪个池子中进行选择。候选池决定了策略的收益来源与风险特征。如果候选池过窄,轮动空间不足;如果候选池过宽,可能引入大量相关性较高或流动性不足的标的,增加交易摩擦与模型噪声。
在ETF轮动场景下,候选池通常可以按以下几类划分:
- 大类资产ETF:股票ETF、债券ETF、商品ETF、货币ETF等;
- 行业ETF:消费、医药、金融、科技、制造等;
- 风格ETF:大盘、中小盘、成长、价值等;
- 跨境或区域ETF:跟踪不同市场指数的ETF。
初始策略可以选择以宽基指数ETF作为起点,例如纳入股票、债券、商品等多类资产,形成资产轮动基础池。这样既能体现轮动逻辑,又便于理解策略在不同市场阶段的表现。
候选池确定后,需要明确调仓频率。常见选择包括周度、双周、月度调仓。频率过高会增加换手与成本,频率过低可能错过中期趋势。初始策略可采用月度调仓,作为基准节奏。
2. 数据获取
数据获取是量化策略中非常关键但容易被低估的环节。数据质量直接影响信号计算与回测结果的可靠性。对于ETF数据,至少需要获取以下字段:
- 交易日期;
- 开盘价、最高价、最低价、收盘价;
- 成交量;
- 复权因子或前复权/后复权价格;
- 标的基础信息,如代码、名称、上市日期等。
使用未复权价格会导致分红、拆分等事件造成价格跳空,进而扭曲动量计算。因此,必须优先使用前复权或后复权价格。若数据源只提供不复权价格与复权因子,则需要自行计算连续价格序列。
数据获取方式可以包括本地数据文件、专业数据终端、公开数据接口等。无论何种来源,都应建立数据校验机制,包括缺失值检查、异常值识别、停牌处理、上市时间少于回看期的标的过滤等。
以下示例展示了一个简单的数据整理逻辑,假设已经获取到每只ETF的日线数据:
import pandas as pddef prepare_price_data(raw_df):"""将原始行情数据处理为多标的收盘价矩阵。输入需包含 code, date, close, adj_factor 等列。"""df = raw_df.copy()df["adj_close"] = df["close"] * df["adj_factor"]price_matrix = df.pivot(index="date", columns="code", values="adj_close")price_matrix = price_matrix.sort_index()price_matrix = price_matrix.dropna(how="all")return price_matrix
上述代码的核心,是将不同ETF的复权收盘价整理为宽表格式,方便后续统一计算收益率与动量指标。
3. 数据计算
动量策略的核心,是根据过去一段时间内的价格表现对候选标的进行排序。最常见的动量窗口包括20日、60日、120日、250日等。较长窗口更能反映中期趋势,但滞后性也更强;较短窗口对价格变化更敏感,但可能受短期噪声影响。
在ETF轮动中,通常不直接使用简单价格涨幅,而是先计算对数收益率或区间收益率,再按时间窗口滚动求和或取均值。例如,计算过去N个交易日的累计收益率:
def momentum_score(price_matrix, lookback=60):"""计算滚动动量得分,默认使用过去60个交易日收益率。"""returns = price_matrix.pct_change()score = returns.rolling(window=lookback).apply(lambda x: (1 + x).prod() - 1, raw=True)return score
需要特别处理的是上市时间不足回看期的标的。一种做法是允许在数据不足时使用较短窗口,另一种做法是直接将该标的动量置为空值,不参与当期排序。初始策略中,可以要求标的至少具备完整回看期数据,否则不纳入本期候选。
动量排序后,通常选择动量最高的前1至3个标的持仓。也可以采用分层方式,将候选标的按动量值分为若干组,只做多最强分层。初始策略为了清晰起见,可仅选择动量最高的1个标的,每期全仓持有。
4. 信号生成与回测
信号生成的关键,是将连续动量得分转换为离散持仓目标,并在调仓日生成交易指令。若采用月度调仓,则每月最后一个交易日计算动量,下一个交易日执行调仓。
回测过程中需要考虑以下因素:
- 交易成本:包括手续费与冲击成本。ETF交易成本通常低于个股,但仍不可忽略;
- 滑点:调仓时可能无法以收盘价完全成交;
- 涨跌停或停牌:部分标的在调仓日无法交易;
- 现金处理:调仓后剩余现金不计收益或按无风险利率处理。
以下是一段简化的回测主循环示意:
def run_backtest(price_matrix, lookback=60, top_k=1, rebalance_freq="M"):"""简化版月度动量轮动回测。price_matrix: 复权收盘价矩阵"""momentum = momentum_score(price_matrix, lookback)rebalance_dates = price_matrix.resample(rebalance_freq).last().indexpositions = pd.Series(index=price_matrix.index, dtype=object)for date in rebalance_dates:if date not in price_matrix.index:continuecurrent_score = momentum.loc[date].dropna()if current_score.empty:continueselected = current_score.nlargest(top_k).index.tolist()next_dates = price_matrix.index[price_matrix.index > date]if len(next_dates) == 0:continuenext_date = next_dates[0]positions.loc[next_date:] = [selected] * len(positions.loc[next_date:])portfolio_returns = []for i in range(1, len(price_matrix.index)):prev_date = price_matrix.index[i - 1]curr_date = price_matrix.index[i]pos = positions.loc[prev_date]if isinstance(pos, list) and len(pos) > 0:daily_ret = price_matrix.loc[curr_date, pos].pct_change().mean()else:daily_ret = 0.0portfolio_returns.append(daily_ret)return pd.Series(portfolio_returns, index=price_matrix.index[1:])
上述代码仅为框架性示意,实际实现中还需处理调仓日对齐、现金、成本等问题。但核心逻辑是清晰的:每期计算动量,选择最强标的,持有至下一调仓日。
5. 结果统计和画图
回测完成后,需要从收益与风险两个维度统计策略表现。常用指标包括:
- 累计收益率;
- 年化收益率;
- 年化波动率;
- 最大回撤;
- 夏普比率;
- 相对基准的超额收益。
基准可以选择等权持有全部候选标的,也可以选择某个宽基指数。对于资产轮动策略,等权基准更能反映候选池的整体表现。
可视化方面,可以绘制策略净值曲线与基准净值曲线,观察二者在不同市场阶段的分化。也可以绘制回撤曲线,展示策略在历史中的回撤区间。若有多标的持仓,还可统计各标的被选中的频率,辅助判断策略是否过度依赖某一类资产。
三、轮动策略改进一:多标的持有与风险分散
初始策略仅选择动量最高的1个标的,虽然逻辑简单,但仓位高度集中。当市场风格快速切换时,单一标的可能经历较大回撤。改进方向之一,是持有动量排名靠前的多个标的,并进行等权或动量加权配置。
例如,将持仓数量从1个扩展至3个,每期选择动量前3名的ETF,等权分配资金。这种改进的收益来源,仍然是动量效应,但通过分散持有降低单标的尾部风险。同时,多标的持有也减少了调仓时对单一品种的过度依赖。
实现上,只需将top_k参数从1调整为3,并在计算组合收益时对多标的日收益取平均。需要注意的是,持仓数量增加后,调仓频率与交易成本可能随之上升,应结合成本模型综合评估。
另一个可改进的点,是在动量排序时引入波动率调整。传统动量只考虑收益,但收益高可能来自高波动。若将收益除以波动率,可得到风险调整后的动量,通常能提高策略稳定性。计算方法上,可以用过去N日收益率的均值除以标准差,或使用夏普比率作为排序指标。
四、轮动策略改进二:趋势过滤与资产上限
初始动量轮动策略在持续下跌市场中,可能仍然持有高波动资产,因为只要其相对动量靠前,就会被选中。但此时绝对收益可能为负。
改进方向之二,是引入趋势过滤。具体做法是对候选资产或指数计算长期均线,例如120日均线。若当前价格位于长期均线下方,则降低该资产权重,或将其移出候选池。这种过滤机制可以避免在系统性下行阶段过度暴露风险。
另一种常见改进,是对单一资产或类别设置持仓上限。例如,即使商品类ETF动量最强,也不允许其占据超过50%的仓位。这样可以防止策略在极端行情下过度集中于某一类高波动资产。
趋势过滤与持仓上限可以组合使用。实现时,可在每期动量排序结束后,先剔除价格低于长期均线的标的,再对剩余标的按动量选择。如果剩余标的数量不足,则将剩余资金配置为货币ETF或现金类资产。
def trend_filter(price_matrix, ma_window=120):"""生成趋势过滤布尔矩阵。True 表示价格位于长期均线上方。"""ma = price_matrix.rolling(window=ma_window).mean()return price_matrix > ma
在回测主循环中,可将该过滤矩阵与动量得分同时使用:
filtered_score = momentum.where(trend_filter)selected = filtered_score.loc[date].dropna().nlargest(top_k)
这样可以确保只在符合条件的标的中进行选择,从而降低系统性下跌阶段的回撤风险。
五、策略局限与进一步研究方向
动量轮动策略在实践中也面临一些值得关注的问题。
首先是参数敏感性。回看期、持仓数量、调仓频率等参数的变化,可能对策略表现产生明显影响。投资者应避免仅根据历史最优参数进行过度拟合。可通过参数稳定性测试或滚动样本外验证来观察策略是否仍然有效。
其次是交易成本与流动性。ETF虽然整体流动性较好,但部分行业或跨境ETF的成交活跃度较低,调仓时可能产生较高冲击成本。在实盘前应重点评估候选池中每一只ETF的日均成交额与买卖价差。
再次是市场环境影响。动量效应在趋势明显、风格持续的市场中可能表现较好,但在频繁震荡或风格快速切变的阶段,策略可能反复止损或追高。因此,动量轮动更适合作为中长期配置工具,而非高频交易手段。
进一步研究方向可以包括:
- 将因子暴露控制在合理范围,例如结合价值、质量等因子形成多因子轮动;
- 在不同资产类别之间设置动态权重边界,避免单一类别过度集中;
- 引入机器学习方法进行动量信号融合,但需警惕过拟合;
- 在云端构建自动化数据更新与回测任务,通过容器、任务调度与监控告警能力实现策略的持续跟踪与预警。
六、总结
本文构建了一套以ETF为标的的中国版动量轮动策略,从候选池设计、数据获取、动量计算、信号生成到回测统计进行了完整说明。初始策略通过月度调仓选择最强标的,随后引入多标的分散持有、波动率调整、趋势过滤与资产上限等改进方法,以提高稳定性和风险控制能力。
需要强调的是,策略回测仅代表历史表现,不能保证未来收益。任何策略在实盘应用前,都应经过充分的样本外验证、成本测算与风险压力测试。本文最大的价值,在于提供了一套可复现的量化轮动研究框架,帮助读者理解动量策略从数据到回测的完整实现路径。
对希望深入实践的读者,建议先将策略逻辑跑通,再逐步加入成本模型、风险控制与自动化监控模块,最终形成可持续迭代的策略研究体系。

登录后可评论,请前往 登录 或 注册