🚀 AI 一键生成 qmt 策略代码

QMT 策略如何同时管理多个账户并接收交易回调?

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

问题描述

怎样才能在一个策略里同时操作好几个资金账户,并且能实时知道每个账户的下单和成交情况?

解决方案

在 QMT 中实现多账户同时交易及实时监控,主要涉及以下三个核心步骤:

  1. 初始化绑定:在 init 函数中,通过循环调用 ContextInfo.set_account() 绑定所有需要操作的资金账号。这是接收交易回报的前提。
  2. 指定账户下单:在 passorder(或 algo_passorder)函数中,明确传入具体的 accountID 参数,而不是留空或使用默认值。
  3. 回调函数监控:利用 order_callback(委托回报)和 deal_callback(成交回报)函数,通过返回对象中的 m_strAccountID 属性来区分是哪个账户的信息。

以下是完整的策略代码示例,展示了如何同时管理两个账户,并分别打印它们的实时交易状态。

多账户交易策略代码示例

# -*- coding: gbk -*-
import time

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 1. 定义需要操作的资金账号列表
    # 请将此处替换为您真实的资金账号
    ContextInfo.my_accounts = ['88000001', '88000002']
    
    # 2. 循环绑定账号
    # 注意:必须在 init 中调用 set_account,否则无法接收主推回调
    for acc_id in ContextInfo.my_accounts:
        ContextInfo.set_account(acc_id)
        print(f"账号 {acc_id} 绑定成功")

    # 设置股票池(示例)
    ContextInfo.set_universe(['600000.SH'])
    
    # 定义一个全局变量用于控制测试下单(防止重复下单)
    ContextInfo.has_ordered = False

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 仅在最后一根K线(实时行情)运行,避免历史回测重复下单
    if not ContextInfo.is_last_bar():
        return

    # 示例:简单的下单逻辑,仅运行一次
    if not ContextInfo.has_ordered:
        stock_code = '600000.SH'
        price = -1 # -1 代表使用 passorder 中 prType 定义的价格类型
        
        print("开始对多个账户进行下单操作...")
        
        # 3. 遍历账户列表,分别下单
        for acc_id in ContextInfo.my_accounts:
            # passorder 参数说明:
            # opType=23(买入), orderType=1101(单股单账号), accountId=acc_id
            # orderCode=stock_code, prType=5(最新价), price=price, volume=100
            passorder(23, 1101, acc_id, stock_code, 5, price, 100, ContextInfo)
            print(f"已向账户 {acc_id} 发送买入指令:{stock_code} 100股")
        
        ContextInfo.has_ordered = True

def order_callback(ContextInfo, orderInfo):
    """
    委托状态变化主推函数
    当任意绑定账号的委托状态发生变化(如已报、部成、已成、废单)时触发
    """
    # 4. 通过 m_strAccountID 区分账户
    account_id = orderInfo.m_strAccountID
    stock_code = orderInfo.m_strInstrumentID
    order_id = orderInfo.m_strOrderSysID
    status = orderInfo.m_nOrderStatus
    msg = orderInfo.m_strStatus # 状态描述文本
    
    print(f"【委托回报】账户:{account_id} | 标的:{stock_code} | 委托号:{order_id} | 状态:{msg} ({status})")

def deal_callback(ContextInfo, dealInfo):
    """
    成交状态变化主推函数
    当任意绑定账号发生成交时触发
    """
    # 4. 通过 m_strAccountID 区分账户
    account_id = dealInfo.m_strAccountID
    stock_code = dealInfo.m_strInstrumentID
    price = dealInfo.m_dPrice
    volume = dealInfo.m_nVolume
    amount = dealInfo.m_dTradeAmount
    
    print(f"【成交回报】账户:{account_id} | 标的:{stock_code} | 均价:{price} | 数量:{volume} | 金额:{amount}")

def account_callback(ContextInfo, accountInfo):
    """
    资金账号状态变化主推
    """
    account_id = accountInfo.m_strAccountID
    available = accountInfo.m_dAvailable # 可用资金
    net_asset = accountInfo.m_dBalance   # 总资产
    
    print(f"【资金回报】账户:{account_id} | 可用资金:{available} | 总资产:{net_asset}")

代码关键点解析

  1. ContextInfo.set_account(acc_id)

    • 这是多账户操作的基础。虽然 passorder 可以直接指定账号下单,但如果不先用 set_account 绑定,策略将无法收到该账号的 order_callbackdeal_callback 回报。
    • 必须在 init 中执行。
  2. passorder 的第三个参数

    • 函数签名:passorder(opType, orderType, accountid, ...)
    • 在单账户策略中,这里通常写 ContextInfo.get_account(0) 或直接写账号字符串。
    • 在多账户策略中,必须在循环中传入当前迭代的 acc_id,这样才能确保指令发往正确的账户。
  3. 回调函数中的 m_strAccountID

    • orderInfo(委托对象)和 dealInfo(成交对象)都包含 m_strAccountID 属性。
    • 在回调函数内部,通过读取这个属性,您可以判断当前这条回报属于哪个资金账号,从而更新对应的策略状态或日志。

常见问题 Q&A

Q: 如果我想对不同的账户交易不同的股票,应该怎么写?
A: 您可以使用字典来管理账户配置。例如在 init 中定义 ContextInfo.acc_config = {'88001': ['600000.SH'], '88002': ['000001.SZ']},然后在 handlebar 中遍历字典,根据账户对应的股票列表进行下单。

Q: passorder 中的 orderType 参数 1201 (账号组下单) 和循环调用 1101 (单账号下单) 有什么区别?
A:

  • 1101 (单账号):需要在代码中写循环,逐个调用 passorder。优点是灵活,可以为每个账户设置不同的下单数量或价格。
  • 1201 (账号组):需要先在 QMT 交易界面的“账号组设置”中建好组,然后传入组名。优点是代码简单,一次调用即可对组内所有账号下单(通常是相同数量或按比例)。如果您需要精细化控制每个账户的交易逻辑,建议使用代码循环 + 1101 模式。

Q: 为什么我的 order_callback 没有触发?
A: 请检查两点:

  1. 是否在 init 函数中对该账号执行了 ContextInfo.set_account()
  2. 策略是否处于“实盘”或“模拟交易”模式(回测模式下回调函数的行为与实盘不同,且通常不涉及真实资金账号交互)。