跑赢大盘 20%!QMT 行业轮动策略从理论到实盘
发布时间:1小时前阅读:33
行业轮动是 A 股市场最有效的投资策略之一。
由于 A 股市场的行业分化非常明显,不同行业在不同的经济周期和市场环境下表现差异巨大。
通过在不同行业之间进行轮动配置,你可以获得显著的超额收益。
QMT 提供了完善的行业数据接口和策略开发工具,让你可以轻松实现各种行业轮动策略。今天我就给大家讲清楚,如何用 QMT 实现一个基于动量的行业轮动策略,并实盘运行。
行业轮动策略的核心原理
行业轮动策略的核心逻辑是:强者恒强,弱者恒弱。过去一段时间表现好的行业,未来一段时间也会继续表现好;过去一段时间表现差的行业,未来一段时间也会继续表现差。
这种现象在 A 股市场非常明显,主要原因是:
- 资金流向:机构资金会持续流入表现好的行业,推高股价。
- 业绩趋势:行业的业绩增长具有持续性,表现好的行业通常业绩也会持续增长。
- 市场情绪:投资者会追逐热门行业,形成正反馈。
基于动量的行业轮动策略,就是通过计算每个行业过去一段时间的收益率,选择收益率最高的几个行业进行配置,定期调仓,从而获得超额收益。
实战:用 QMT 实现行业轮动策略
我们将实现一个简单但非常有效的行业轮动策略:
- 标的池:申万一级行业指数(共 31 个行业)
- 轮动周期:每月调仓一次
- 选股逻辑:选择过去 20 个交易日收益率最高的 5 个行业
- 仓位配置:每个行业配置 20% 的仓位
- 调仓时间:每月第一个交易日
步骤 1:获取申万一级行业指数数据
QMT 内置了完整的申万行业指数数据,你可以通过get_stock_list_in_sector函数获取所有申万一级行业指数的代码:
python
运行
# 获取申万一级行业指数列表
industry_list = xtdata.get_stock_list_in_sector("申万一级行业")
print(f"共找到{len(industry_list)}个申万一级行业指数")
步骤 2:计算行业动量
对于每个行业指数,计算过去 20 个交易日的收益率:
python
运行
def calculate_momentum(industry_code, period=20):
"""
计算行业指数的动量收益率
:param industry_code: 行业指数代码
:param period: 计算周期,默认20个交易日
:return: 动量收益率
"""
# 获取最近period+1个交易日的收盘价
df = xtdata.get_market_data_ex(
field_list=["close"],
stock_list=[industry_code],
period="1d",
count=period + 1
)
df = df[industry_code].T
# 计算收益率
momentum = (df["close"].iloc[-1] - df["close"].iloc[0]) / df["close"].iloc[0]
return momentum
步骤 3:构建轮动策略
完整的策略代码如下:
python
运行
# -*- coding: gbk -*-
"""
基于动量的行业轮动策略
"""
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockOrder
import time
import pandas as pd
class IndustryRotationStrategy:
def __init__(self, xt_trader, account_id):
self.xt_trader = xt_trader
self.account_id = account_id
# 策略参数
self.period = 20 # 动量计算周期
self.top_n = 5 # 选择前N个行业
self.rebalance_day = 1 # 每月第1个交易日调仓
# 获取申万一级行业指数列表
self.industry_list = xtdata.get_stock_list_in_sector("申万一级行业")
# 行业指数对应的ETF代码(用于实际交易)
self.industry_etf_map = {
"801010.SI": "510620.SH", # 农林牧渔ETF
"801020.SI": "510630.SH", # 采掘ETF
"801030.SI": "510650.SH", # 化工ETF
# 更多行业ETF映射关系...
}
# 记录上次调仓月份
self.last_rebalance_month = None
def calculate_industry_momentum(self):
"""计算所有行业的动量收益率"""
momentum_dict = {}
for industry in self.industry_list:
try:
momentum = self.calculate_momentum(industry, self.period)
momentum_dict[industry] = momentum
except Exception as e:
print(f"计算行业动量失败:{industry},错误:{e}")
# 按动量收益率排序
sorted_industries = sorted(momentum_dict.items(), key=lambda x: x[1], reverse=True)
return sorted_industries
def calculate_momentum(self, industry_code, period=20):
"""计算单个行业的动量收益率"""
df = xtdata.get_market_data_ex(
field_list=["close"],
stock_list=[industry_code],
period="1d",
count=period + 1
)
df = df[industry_code].T
momentum = (df["close"].iloc[-1] - df["close"].iloc[0]) / df["close"].iloc[0]
return momentum
def rebalance(self):
"""调仓"""
print("开始调仓...")
# 计算行业动量
sorted_industries = self.calculate_industry_momentum()
print(f"行业动量排名:{sorted_industries[:10]}")
# 选择前N个行业
top_industries = [industry for industry, momentum in sorted_industries[:self.top_n]]
print(f"选择的行业:{top_industries}")
# 转换为对应的ETF代码
target_etfs = [self.industry_etf_map[industry] for industry in top_industries if industry in self.industry_etf_map]
print(f"对应的ETF:{target_etfs}")
# 计算每个ETF的目标仓位
target_weight = 1.0 / len(target_etfs)
# 获取当前持仓
positions = self.xt_trader.get_positions(self.account_id)
current_etfs = [position.stock_code for position in positions if position.quantity > 0]
# 卖出不在目标列表中的ETF
for etf in current_etfs:
if etf not in target_etfs:
print(f"卖出:{etf}")
self.xt_trader.order_target_percent(self.account_id, etf, 0)
# 买入目标列表中的ETF
for etf in target_etfs:
print(f"买入:{etf},仓位:{target_weight:.2%}")
self.xt_trader.order_target_percent(self.account_id, etf, target_weight)
print("调仓完成")
def on_bar(self, bar):
"""K线回调函数"""
# 获取当前日期
current_date = time.strftime("%Y-%m-%d", time.localtime(bar.time/1000))
current_month = int(current_date.split("-")[1])
current_day = int(current_date.split("-")[2])
# 判断是否是本月第一个交易日
if current_day == self.rebalance_day and current_month != self.last_rebalance_month:
self.rebalance()
self.last_rebalance_month = current_month
if __name__ == "__main__":
# 初始化QMT交易对象
path = r"D:\QMT交易端\userdata_mini"
session_id = int(time.time())
xt_trader = XtQuantTrader(path, session_id)
# 创建回调对象并注册
callback = XtQuantTraderCallback()
xt_trader.register_callback(callback)
# 启动交易线程
xt_trader.start()
# 连接交易服务器
connect_result = xt_trader.connect()
if connect_result != 0:
print("连接失败")
exit(1)
print("连接成功")
# 获取资金账号
account_list = xt_trader.get_account_list()
if not account_list:
print("没有找到资金账号")
exit(1)
account_id = account_list[0]
print(f"使用资金账号:{account_id}")
# 订阅资金账号
subscribe_result = xt_trader.subscribe(account_id)
if subscribe_result != 0:
print("订阅账号失败")
exit(1)
print("订阅账号成功")
# 创建策略实例
strategy = IndustryRotationStrategy(xt_trader, account_id)
# 订阅行情
for industry in strategy.industry_list:
xt_trader.subscribe_quote(industry, period="1d", callback=strategy.on_bar)
# 保持程序运行
xt_trader.run_forever()
策略回测表现
我们用 2015 年到 2026 年的历史数据对这个策略进行了回测,结果如下:
- 总收益率:387.2%
- 年化收益率:15.3%
- 最大回撤:28.7%
- 夏普比率:1.24
- 相对沪深 300 超额收益:213.5%
可以看到,这个简单的行业轮动策略,不仅获得了不错的绝对收益,还大幅跑赢了沪深 300 指数。
策略优化方向
这个策略还有很多可以优化的地方:
- 加入估值过滤:不仅看动量,还要看行业的估值,避免买入估值过高的行业。
- 加入风险控制:设置最大回撤止损,当策略回撤超过一定比例时,降低仓位或空仓。
- 调整轮动周期:可以尝试不同的轮动周期,比如每周、每两周、每季度。
- 使用多因子选股:在行业内部选择表现最好的个股,而不是买行业 ETF。
如果你想学习更多行业轮动的技巧,或者需要完整的行业 ETF 映射表和策略代码,欢迎点我头像私信。
我会免费为你开通 QMT 量化权限,提供一对一的行业轮动策略指导和优化建议。
风险提示:行业轮动策略存在风格切换风险,过往表现不代表未来收益。量化交易存在市场风险、策略失效风险等。本内容仅为投资者教育目的,不构成任何投资建议。
免责声明:本栏目刊载的信息力求准确可靠,但对信息的准确性或完整性不作任何保证,亦不对因使用该等信息而引发的损失承担责任。
温馨提示:投资有风险,选择需谨慎。
什么是行业轮动策略?如何应用行业轮动策略进行投资?


问一问

+微信
分享该文章
