问题描述
解决方案
这是一个基于 QMT (Quant My Trading) 平台的 全天候(All-Weather)投资组合策略 的 Python 实现代码。
策略核心逻辑
全天候策略由桥水基金(Bridgewater)创始人瑞·达利欧提出,旨在通过配置不同相关性的资产,在各种经济环境(通胀、通缩、经济增长、经济衰退)下都能获得相对稳定的收益。
针对中国市场的 ETF 配置示例(简化版):
- 股票 (30%): 代表经济增长。使用 沪深300 ETF (510300.SH)。
- 长期债券 (40%): 对抗通缩和经济衰退。使用 十年国债 ETF (511260.SH)。
- 中期债券 (15%): 提供基础收益和防御。使用 五年国债 ETF (511010.SH)。
- 黄金/大宗商品 (15%): 对抗通胀。使用 黄金 ETF (518880.SH)。
策略运行机制
- 周期: 日线 (1d)。
- 调仓频率: 每 20 个交易日(约一个月)进行一次再平衡,使各资产回归目标权重。
- 交易接口: 使用
order_target_percent按照账户总资产比例下单。
QMT 策略代码
# -*- coding: gbk -*-
import pandas as pd
import time
def init(ContextInfo):
"""
策略初始化函数
"""
# 1. 设置资金账号 (请替换为您自己的实盘或模拟账号)
# 注意:回测模式下,此账号仅作为标识,资金由回测参数设定
ContextInfo.account_id = '6000000000'
ContextInfo.set_account(ContextInfo.account_id)
# 2. 定义全天候策略的资产组合及目标权重
# 键为ETF代码,值为目标百分比权重
ContextInfo.target_portfolio = {
'510300.SH': 0.30, # 股票:沪深300 ETF
'511260.SH': 0.40, # 长债:十年国债 ETF
'511010.SH': 0.15, # 中债:五年国债 ETF
'518880.SH': 0.15 # 商品/黄金:黄金 ETF
}
# 3. 设置股票池 (用于行情订阅和回测数据下载)
ContextInfo.set_universe(list(ContextInfo.target_portfolio.keys()))
# 4. 设置调仓参数
ContextInfo.rebalance_days = 20 # 每20个交易日调仓一次
ContextInfo.days_counter = 0 # 计数器
print("全天候策略初始化完成")
print("目标组合: ", ContextInfo.target_portfolio)
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 仅在K线结束时运行策略逻辑,避免盘中Tick波动频繁触发
if not ContextInfo.is_last_bar():
return
# 获取当前K线索引
bar_index = ContextInfo.barpos
# 获取当前时间
current_time = ContextInfo.get_bar_timetag(bar_index)
current_date = timetag_to_datetime(current_time, '%Y%m%d')
# 计数器递增
ContextInfo.days_counter += 1
# 判断是否达到调仓周期
# 逻辑:每 rebalance_days 天,或者第一天运行的时候进行调仓
if ContextInfo.days_counter % ContextInfo.rebalance_days == 0 or ContextInfo.days_counter == 1:
print(f"日期: {current_date},触发调仓信号,开始执行再平衡...")
rebalance(ContextInfo)
def rebalance(ContextInfo):
"""
再平衡执行函数:将持仓调整至目标权重
"""
# 获取目标组合
targets = ContextInfo.target_portfolio
account_id = ContextInfo.account_id
# 遍历目标组合中的每个标的
for stock_code, target_weight in targets.items():
# 获取当前标的的最新价格,用于计算(虽然order_target_percent内部会自动处理,但为了日志记录可以获取一下)
# 注意:order_target_percent 是按照总资产(现金+持仓市值)的比例下单
# 检查标的是否停牌,停牌则跳过
if ContextInfo.is_suspended_stock(stock_code):
print(f"标的 {stock_code} 停牌,跳过调仓")
continue
# 执行下单:调整至目标比例
# order_target_percent 会自动计算买卖方向和数量
# 1101 代表单股/单账号/普通/股数下单方式 (此处函数内部会转换为按比例计算出的股数)
# 注意:QMT的 order_target_percent 在回测和实盘中表现一致
print(f"调整 {stock_code} 仓位至 {target_weight * 100}%")
# 调用 QMT 的目标比例下单函数
# 参数说明: 标的代码, 目标比例(0-1), 下单选价类型(5为最新价), 价格(-1为无效), ContextInfo, 账号
order_target_percent(stock_code, target_weight, 5, -1, ContextInfo, account_id)
def timetag_to_datetime(timetag, format_str):
"""
辅助函数:将时间戳转换为日期字符串
"""
import time
return time.strftime(format_str, time.localtime(timetag / 1000))
代码详细解析
-
init(ContextInfo)初始化:ContextInfo.target_portfolio: 这里定义了核心资产配置。你可以根据实际需求修改 ETF 代码。例如,如果你想增加风险偏好,可以提高510300.SH的权重。ContextInfo.set_universe: 这一步至关重要,它告诉 QMT 系统需要准备哪些标的的历史数据。ContextInfo.rebalance_days: 设定为 20,意味着大约每个月进行一次仓位检查和调整。
-
handlebar(ContextInfo)核心循环:ContextInfo.is_last_bar(): 确保逻辑只在 K 线走完时(或回测的每一天结束时)运行一次,防止盘中重复发单。- 调仓逻辑: 使用取模运算
%来判断是否到达第 20 天。
-
rebalance(ContextInfo)交易执行:order_target_percent: 这是 QMT 中非常强大的函数。- 如果当前该资产持仓为 0,它会买入直到达到目标比例(例如 30%)。
- 如果当前持仓因价格上涨变成了 35%,它会自动卖出多余的 5%,使其回归 30%。
- 如果当前持仓因价格下跌变成了 25%,它会自动买入补足至 30%。
- 停牌检查: 使用
is_suspended_stock防止对停牌股票下单导致报错或废单。
使用注意事项
-
数据补充:
-
实盘配置:
- 在
init函数中,将ContextInfo.account_id修改为你真实的资金账号。 - 在 QMT 界面右侧的【模型交易】或【回测】面板中,设置好初始资金。
- 在
-
ETF 最小交易单位:
- ETF 交易单位通常为 100 份(1手)。如果账户总资金太少(例如只有几千元),可能无法精确分配 7.5% 或 15% 的比例,导致分配失败。建议资金量至少在 5万元以上以获得较好的拟合效果。
-
费率设置:
- ETF 交易通常免印花税,佣金较低。在回测设置中,建议将费率设置得比股票低一些(例如万分之一或万分之二),以更接近真实情况。
常见问题 (Q&A)
Q: 为什么选择 ETF 而不是直接买股票或债券?
A: 在 QMT 量化中,ETF 是实现大类资产配置最便捷的工具。它们流动性好,交易费用低,且能通过一个代码覆盖一篮子资产(如沪深300),非常适合全天候策略。
Q: 如何修改调仓频率?
A: 修改 init 函数中的 ContextInfo.rebalance_days 变量。例如,改为 5 就是每周调仓,改为 60 就是每季度调仓。
Q: 这个策略能保证盈利吗?
A: 全天候策略的目标是降低波动率和最大回撤,而不是追求短期暴利。在股市大牛市中,它可能跑不赢纯股票策略;但在股市崩盘或经济衰退时,它的表现通常会优于纯股票策略。
Q: order_target_percent 是按什么基准计算的?
A: 它是按照账户总资产(可用资金 + 持仓市值)来计算的。例如账户总资产 100万,目标权重 0.3,系统会确保该标的持仓市值调整为 30万。