🚀 AI 一键生成 qmt 策略代码

如何使用 QMT 实现投资组合的自动化再平衡策略?

qmt | 发布时间: 2025/12/2
以下内容由 EasyQuant 生成。

问题描述

请帮我编写一个投资组合再平衡策略,定期将不同资产的持仓调整回预设的目标权重。

解决方案

这是一个基于 QMT 平台的投资组合再平衡策略(Portfolio Rebalancing Strategy)。

策略逻辑

  1. 目标设定:在 init 中设定一个目标投资组合及其对应的权重(例如:60% 股票A,40% 股票B)。
  2. 定期检查:在 handlebar 中,根据设定的周期(如每月第一个交易日)触发调仓逻辑。
  3. 执行调仓:使用 QMT 提供的 order_target_percent 函数。该函数会自动计算当前持仓价值与目标价值的差额,并自动下单买入或卖出,使该标的占总资产的比例达到预设值。

策略代码

# -*- coding: gbk -*-
import pandas as pd

def init(ContextInfo):
    """
    初始化函数,设定账户、股票池、目标权重和调仓周期
    """
    # 1. 设置资金账号 (请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.account_type = 'STOCK' # 账号类型:STOCK-股票,FUTURE-期货
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 2. 设定目标投资组合及权重 (权重之和建议为 1.0)
    # 示例:贵州茅台 40%, 宁德时代 30%, 五粮液 30%
    ContextInfo.target_weights = {
        '600519.SH': 0.4,
        '300750.SZ': 0.3,
        '000858.SZ': 0.3
    }
    
    # 3. 设置股票池 (用于回测时下载数据)
    ContextInfo.set_universe(list(ContextInfo.target_weights.keys()))
    
    # 4. 策略运行参数
    ContextInfo.rebalance_period = 'monthly'  # 调仓周期: 'monthly' (月度) 或 'weekly' (周度)
    ContextInfo.last_rebalance_date = None    # 记录上次调仓日期
    
    print("策略初始化完成,目标组合权重:", ContextInfo.target_weights)

def handlebar(ContextInfo):
    """
    行情驱动函数,每根K线运行一次
    """
    # 获取当前K线的时间
    index = ContextInfo.barpos
    timetag = ContextInfo.get_bar_timetag(index)
    current_date_str = timetag_to_datetime(timetag, '%Y%m%d')
    
    # 检查是否需要调仓
    if not check_rebalance_condition(ContextInfo, current_date_str):
        return

    print(f"[{current_date_str}] 触发再平衡调仓...")
    
    # 执行调仓逻辑
    rebalance_portfolio(ContextInfo)
    
    # 更新上次调仓时间
    ContextInfo.last_rebalance_date = current_date_str

def check_rebalance_condition(ContextInfo, current_date_str):
    """
    判断是否满足调仓时间条件
    """
    # 如果是第一天运行,直接调仓
    if ContextInfo.last_rebalance_date is None:
        return True
    
    last_date = ContextInfo.last_rebalance_date
    
    # 月度调仓:判断当前月份是否与上次调仓月份不同
    if ContextInfo.rebalance_period == 'monthly':
        current_month = current_date_str[0:6] # 取 YYYYMM
        last_month = last_date[0:6]
        if current_month != last_month:
            return True
            
    # 周度调仓:这里简单判断是否跨周(实际应用可结合 datetime 库判断周几)
    # 为了演示简单,此处仅实现月度逻辑,如需周度可扩展
    
    return False

def rebalance_portfolio(ContextInfo):
    """
    执行核心调仓逻辑
    """
    # 1. 获取当前账户的总资产 (用于计算目标持仓市值,但在 order_target_percent 中通常自动处理)
    # 注意:order_target_percent 是基于账户总资产(现金+市值)计算比例
    
    # 2. 遍历目标权重字典进行调仓
    for stock_code, target_weight in ContextInfo.target_weights.items():
        # 获取当前标的最新价,用于日志输出或检查停牌
        last_price = ContextInfo.get_market_data_ex(
            ['close'], [stock_code], period=ContextInfo.period, count=1
        )
        
        if stock_code in last_price and not last_price[stock_code].empty:
            # 使用 order_target_percent 函数
            # 该函数会自动计算:(总资产 * target_weight) / 股价 = 目标股数
            # 然后自动下单买入或卖出差额
            order_target_percent(
                stock_code, 
                target_weight, 
                ContextInfo, 
                ContextInfo.account_id
            )
            print(f"  -> 调整 {stock_code} 仓位至目标权重: {target_weight*100}%")
        else:
            print(f"  -> 警告: 无法获取 {stock_code} 行情,跳过调仓")

    # 3. (可选) 清理不在目标列表中的持仓
    # 如果您希望卖出所有不在 target_weights 中的股票,需要获取当前持仓并遍历
    # 此处为简易版,仅处理 target_weights 中定义的股票

代码关键点解析

  1. order_target_percent:

    • 这是 QMT 中实现再平衡最核心的函数。
    • 功能:它不需要你手动计算“我要买多少股”或“我要卖多少股”。你只需要告诉它“我希望这只股票占我总资产的 30%”,函数内部会自动获取你的总资产、当前股价、当前持仓,然后计算差值并下单。
    • 注意:回测模式下,它基于回测虚拟资金计算;实盘模式下,它基于绑定的资金账号的实际总资产计算。
  2. 调仓时间控制 (check_rebalance_condition):

    • handlebar 中,我们通过比较当前 K 线日期的月份和上一次调仓日期的月份。
    • 如果月份发生了变化(例如从 4 月变成了 5 月),则意味着这是新月份的第一个交易日(或第一个 K 线),触发调仓。
  3. 数据获取:

    • 使用了 get_market_data_ex 来简单检查行情是否存在,防止对停牌或无数据的股票下单导致报错。

如何使用

  1. 打开 QMT 客户端,进入“模型研究”或“策略编辑器”。
  2. 新建一个 Python 策略。
  3. 将上述代码复制粘贴进去。
  4. 修改账号:将 YOUR_ACCOUNT_ID 替换为您真实的资金账号。
  5. 修改组合:在 ContextInfo.target_weights 中修改您想要的股票代码和权重。
  6. 运行/回测
    • 回测:设置好回测区间、初始资金和费率,点击“回测”。
    • 实盘:请务必先在模拟账户中测试无误后,再谨慎用于实盘。

常见问题 (Q&A)

Q: 如果我想把某只股票彻底清仓怎么办?
A:ContextInfo.target_weights 中将该股票的权重设置为 0.0,策略在下次调仓时会调用 order_target_percent(code, 0, ...),从而卖出该股票的所有持仓。

Q: order_target_percent 是按什么价格下单的?
A: 默认情况下,QMT 的交易函数在回测中通常以当前 K 线的收盘价(或次日开盘价,取决于回测机制设置)成交。在实盘中,如果不指定 styleprice 参数,通常默认以最新价(LATEST)下单。如果需要指定限价或对手价,可以在函数中增加参数,例如:order_target_percent(..., style='COMPETE', ...) 使用对手价。

Q: 为什么回测时第一天就买入了?
A: 代码逻辑中 if ContextInfo.last_rebalance_date is None: return True 确保了策略在开始运行的第一天就会进行一次建仓,将现金转换为股票组合。