问题描述
解决方案
在 QMT 中实现多账户同时交易及实时监控,主要涉及以下三个核心步骤:
- 初始化绑定:在
init函数中,通过循环调用ContextInfo.set_account()绑定所有需要操作的资金账号。这是接收交易回报的前提。 - 指定账户下单:在
passorder(或algo_passorder)函数中,明确传入具体的accountID参数,而不是留空或使用默认值。 - 回调函数监控:利用
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}")
代码关键点解析
-
ContextInfo.set_account(acc_id):- 这是多账户操作的基础。虽然
passorder可以直接指定账号下单,但如果不先用set_account绑定,策略将无法收到该账号的order_callback和deal_callback回报。 - 必须在
init中执行。
- 这是多账户操作的基础。虽然
-
passorder的第三个参数:- 函数签名:
passorder(opType, orderType, accountid, ...) - 在单账户策略中,这里通常写
ContextInfo.get_account(0)或直接写账号字符串。 - 在多账户策略中,必须在循环中传入当前迭代的
acc_id,这样才能确保指令发往正确的账户。
- 函数签名:
-
回调函数中的
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: 请检查两点:
- 是否在
init函数中对该账号执行了ContextInfo.set_account()。 - 策略是否处于“实盘”或“模拟交易”模式(回测模式下回调函数的行为与实盘不同,且通常不涉及真实资金账号交互)。