问题描述
解决方案
编写一个按月度或季度自动调仓的策略,核心逻辑在于时间周期的判断和目标仓位的调整。
在 QMT 中,实现这一功能的最佳方式是利用 handlebar 函数监控时间变化,并使用 order_target_percent(按目标比例下单)函数来自动调整持仓。
策略设计思路
- 初始化 (
init):- 设定股票池和目标权重(例如:沪深300ETF 占 60%,中证500ETF 占 40%)。
- 设定调仓周期(月度或季度)。
- 初始化一个变量记录上一次调仓的月份。
- 行情驱动 (
handlebar):- 获取当前 K 线的时间。
- 判断当前月份是否与上一次调仓月份不同。
- 如果是月度策略:只要月份变化即调仓。
- 如果是季度策略:月份变化且当前月份为 1, 4, 7, 10 月时调仓。
- 执行调仓 (
rebalance):- 遍历目标投资组合。
- 使用
order_target_percent函数,将持仓调整至目标比例。该函数会自动计算需要买入或卖出的数量。
QMT 策略代码实现
以下是一个完整的 Python 策略代码。你可以直接将其复制到 QMT 的策略编辑器中。
# -*- coding: gbk -*-
import time
def init(ContextInfo):
"""
策略初始化函数
"""
# 1. 设置资金账号 (请替换为您的实际账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 2. 设定投资组合及目标权重 (示例:股债平衡策略)
# 格式:{'股票代码': 目标仓位比例},总和建议为 1.0 (100%)
ContextInfo.target_portfolio = {
'510300.SH': 0.6, # 沪深300ETF 占 60%
'511260.SH': 0.4 # 十年国债ETF 占 40%
}
# 设置股票池,确保能获取到这些标的的数据
ContextInfo.set_universe(list(ContextInfo.target_portfolio.keys()))
# 3. 设置调仓频率参数
# 可选值: 'month' (月度), 'quarter' (季度)
ContextInfo.rebalance_freq = 'month'
# 4. 记录上一次调仓的月份,初始化为 -1
ContextInfo.last_rebalance_month = -1
print("策略初始化完成,调仓频率:{}".format(ContextInfo.rebalance_freq))
def handlebar(ContextInfo):
"""
K线运行函数,每根K线执行一次
"""
# 获取当前K线的时间戳
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
# 将时间戳转换为日期字符串,格式 YYYYMMDD
date_str = timetag_to_datetime(timetag, '%Y%m%d')
# 提取当前月份 (int类型)
current_month = int(date_str[4:6])
# 判断是否需要调仓
need_rebalance = False
# 如果当前月份与上一次调仓月份不同,说明进入了新的月份
if current_month != ContextInfo.last_rebalance_month:
if ContextInfo.rebalance_freq == 'month':
# 月度调仓:每个月变化都触发
need_rebalance = True
elif ContextInfo.rebalance_freq == 'quarter':
# 季度调仓:仅在 1, 4, 7, 10 月触发
if current_month in [1, 4, 7, 10]:
need_rebalance = True
# 执行调仓逻辑
if need_rebalance:
print("=" * 30)
print(f"触发调仓信号 - 日期: {date_str}, 月份: {current_month}")
rebalance(ContextInfo)
# 更新上一次调仓月份,防止当月重复调仓
ContextInfo.last_rebalance_month = current_month
def rebalance(ContextInfo):
"""
执行调仓的核心函数
"""
# 遍历目标组合中的每一只股票和权重
for stock_code, target_weight in ContextInfo.target_portfolio.items():
# 使用 order_target_percent 接口
# 该接口会自动计算当前持仓价值与目标价值的差额,并进行买卖
# 参数说明:
# stock_code: 代码
# target_weight: 目标比例 (0.6 代表 60%)
# style: 下单选价类型,'LATEST' 为最新价,'FIX' 为指定价
# price: 价格,使用最新价时填 -1
order_target_percent(stock_code, target_weight, 'LATEST', -1, ContextInfo, ContextInfo.accID)
print(f"调整标的: {stock_code}, 目标权重: {target_weight * 100}%")
# 可选:如果投资组合中有股票被剔除,需要在这里处理卖出逻辑
# (本示例假设组合固定,若组合动态变化,需额外遍历持仓列表卖出不在目标池的股票)
代码关键点解析
-
timetag_to_datetime:- QMT 的
get_bar_timetag返回的是毫秒级时间戳。 - 使用
timetag_to_datetime(timetag, '%Y%m%d')将其转换为易于处理的字符串格式(如 "20231001"),方便提取月份。
- QMT 的
-
调仓频率控制:
- 通过
current_month != ContextInfo.last_rebalance_month判断是否进入了新月份。 - 月度:只要月份变动就调仓。
- 季度:在月份变动的基础上,增加
current_month in [1, 4, 7, 10]的判断,确保只在季度初调仓。
- 通过
-
order_target_percent(核心交易函数):- 这是实现定期平衡策略最强大的函数。
- 它会自动读取当前账户的总资产(现金+持仓市值)。
- 计算公式:
目标持仓市值 = 总资产 * 目标权重。 - 它会自动比较
当前持仓和目标持仓,如果当前持仓不足则买入,过多则卖出,一步到位。
注意事项
- 回测与实盘:
- 回测:请在策略编辑器的“回测参数”中设置好初始资金和费率。建议使用“日线”周期进行回测。
- 实盘:请务必将
ContextInfo.accID修改为您真实的资金账号,并确保 QMT 客户端已登录交易柜台。
- 运行周期:
- 建议将策略运行在 日线 (1d) 周期上。这样
handlebar每天运行一次,会在每个月/季度的第一个交易日触发调仓。
- 建议将策略运行在 日线 (1d) 周期上。这样
- 非交易时间:
- 如果在盘中运行,
order_target_percent会按最新价下单。如果在盘后运行(非交易时间),信号会生成但无法成交,除非使用do_order或等到第二天开盘。
- 如果在盘中运行,
- 资金容量:
order_target_percent是基于账户总资产计算的。如果账户中有其他非策略持仓,可能会影响计算结果。建议该账号专用于此策略,或在代码中手动计算可用资金。
常见问题 Q&A
Q: 如何将策略改为每年调仓一次?
A: 在 handlebar 中修改判断逻辑,提取年份,判断年份是否变化即可。
Q: 如果我想在每月的最后一个交易日调仓怎么办?
A: 可以使用 ContextInfo.get_trading_dates 获取交易日历,判断当前日期是否为当月列表中的最后一个日期。
Q: order_target_percent 会自动卖出不在 target_portfolio 里的股票吗?
A: 不会。该函数只针对传入的 stock_code 进行调整。如果你想清仓不在目标列表里的股票,需要先遍历 ContextInfo.get_trade_detail_data 获取当前持仓,将不在目标列表中的股票权重设为 0 并调用下单函数。