问题描述
解决方案
在 QMT 中管理多个交易账户并执行相同操作,主要有两种实现方式:
- Python 循环方式(推荐):在代码中定义账户列表,通过循环遍历列表,对每个账户单独调用下单函数。这种方式灵活性高,便于单独控制每个账户的资金和风控,且日志清晰。
- QMT 账号组方式:利用
passorder函数的orderType参数(如1201),直接传入账号组名称或以逗号分隔的账号字符串。
下面我将提供一个完整的 Python 循环方式 的策略示例。该策略会定义一个账户列表,并使用简单的双均线策略(金叉买入、死叉卖出)来驱动所有账户同步交易。
策略代码示例
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
"""
初始化函数
"""
# 1. 定义需要管理的账户列表
# 请在此处填写您实际的资金账号,可以是股票账号或期货账号
ContextInfo.account_list = ['60000001', '60000002']
# 2. 设定账号类型
# 'STOCK': 股票, 'FUTURE': 期货
ContextInfo.account_type = 'STOCK'
# 3. 绑定账号以接收回报(重要:循环绑定所有账号)
for acc in ContextInfo.account_list:
ContextInfo.set_account(acc)
# 4. 设置股票池(示例:平安银行)
ContextInfo.stock_code = '000001.SZ'
ContextInfo.set_universe([ContextInfo.stock_code])
# 5. 策略参数
ContextInfo.short_period = 5 # 短周期
ContextInfo.long_period = 10 # 长周期
ContextInfo.trade_vol = 100 # 每次交易数量(股)
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前K线位置
index = ContextInfo.barpos
# 获取历史行情数据 (使用 get_market_data_ex 获取更稳定)
# 获取过去 long_period + 2 根K线,确保有足够数据计算均线
data = ContextInfo.get_market_data_ex(
['close'],
[ContextInfo.stock_code],
period=ContextInfo.period,
count=ContextInfo.long_period + 2,
dividend_type='front'
)
# 提取收盘价序列
if ContextInfo.stock_code not in data:
return
close_series = data[ContextInfo.stock_code]['close']
# 确保数据长度足够
if len(close_series) < ContextInfo.long_period:
return
# 计算均线
ma_short = close_series.rolling(ContextInfo.short_period).mean()
ma_long = close_series.rolling(ContextInfo.long_period).mean()
# 获取最新和上一个时间点的均线值
current_short = ma_short.iloc[-1]
prev_short = ma_short.iloc[-2]
current_long = ma_long.iloc[-1]
prev_long = ma_long.iloc[-2]
# 交易信号判断
# 金叉:短线上穿长线
buy_signal = (prev_short <= prev_long) and (current_short > current_long)
# 死叉:短线下穿长线
sell_signal = (prev_short >= prev_long) and (current_short < current_long)
# 如果是最后一根K线(实时行情),则执行交易逻辑
if ContextInfo.is_last_bar():
if buy_signal:
print(f"触发买入信号: {ContextInfo.stock_code}")
# 遍历账户列表,对每个账户执行买入
for acc_id in ContextInfo.account_list:
trade_func(ContextInfo, acc_id, 'buy')
elif sell_signal:
print(f"触发卖出信号: {ContextInfo.stock_code}")
# 遍历账户列表,对每个账户执行卖出
for acc_id in ContextInfo.account_list:
trade_func(ContextInfo, acc_id, 'sell')
def trade_func(ContextInfo, account_id, action):
"""
自定义下单执行函数
"""
stock_code = ContextInfo.stock_code
volume = ContextInfo.trade_vol
# 构造下单参数
# opType: 23-买入, 24-卖出
# orderType: 1101-单股单账号普通股数下单
# prType: 5-最新价 (也可以用 11-指定价)
if action == 'buy':
opType = 23
print(f"账号 [{account_id}] 执行买入 {stock_code} {volume}股")
passorder(opType, 1101, account_id, stock_code, 5, -1, volume, ContextInfo)
elif action == 'sell':
# 卖出前可以先检查持仓(可选优化)
# 这里直接发送卖出指令
opType = 24
print(f"账号 [{account_id}] 执行卖出 {stock_code} {volume}股")
passorder(opType, 1101, account_id, stock_code, 5, -1, volume, ContextInfo)
代码核心逻辑解析
-
账户列表定义 (
ContextInfo.account_list):
在init函数中,我们将所有需要管理的资金账号存储在一个 Python 列表(List)中。 -
循环绑定 (
set_account):
QMT 的set_account接口用于绑定账号以接收交易回报(如成交、委托状态变化)。为了确保所有账号都能接收回报,我们在init中遍历列表并逐一绑定。 -
循环下单 (
trade_func):
当策略触发买卖信号时,不直接调用passorder,而是调用自定义的trade_func。在该函数内部,我们遍历ContextInfo.account_list,针对每个account_id调用一次passorder。passorder的orderType参数设置为1101,代表“单股、单账号、普通、股/手方式下单”。这是最基础且最稳健的下单方式。
替代方案:使用账号组下单 (orderType 12xx)
如果您希望使用 QMT 原生的账号组功能,可以修改 trade_func 中的 passorder 调用方式。
修改方法:
- 将所有账号拼接成一个字符串,用逗号分隔,例如
"60000001,60000002"。 - 将
passorder的orderType参数改为1201(单股、账号组、普通、股/手方式下单)。
代码片段示例:
# 将列表转换为逗号分隔的字符串
account_group_str = ",".join(ContextInfo.account_list)
# 使用 1201 模式下单,accountID 参数传入拼接好的字符串
# 这条指令会同时对组内所有账号下单
passorder(23, 1201, account_group_str, stock_code, 5, -1, volume, ContextInfo)
两种方式对比:
- 循环方式 (1101):代码可控性更强,可以在循环中加入判断逻辑(例如:账号A买100股,账号B资金多买200股),且如果某个账号报错不会影响其他账号。
- 账号组方式 (1201):代码更简洁,由 QMT 底层处理分发,适合所有账号完全同权重的简单场景。
常见问题 Q&A
Q: 如何在策略中查询每个账户的资金情况?
A: 可以使用 get_trade_detail_data 函数。
# 查询指定账号的资金
acct_info = get_trade_detail_data('60000001', 'stock', 'account')
for i in acct_info:
print(f"账号: {i.m_strAccountID}, 可用资金: {i.m_dAvailable}")
Q: 如果我想让不同账户买入不同的数量怎么办?
A: 建议使用循环方式。您可以定义一个字典来配置每个账户的交易量:
# 定义账户和对应的交易量
ContextInfo.account_config = {
'60000001': 100,
'60000002': 500
}
# 在循环中读取对应的数量
for acc_id, vol in ContextInfo.account_config.items():
passorder(23, 1101, acc_id, stock_code, 5, -1, vol, ContextInfo)
Q: 为什么我的策略没有下单?
A: 请检查以下几点:
ContextInfo.account_list中的账号是否已在 QMT 交易终端登录。passorder中的opType是否正确(股票买入是 23,卖出是 24)。- 是否处于回测模式。回测模式下多账号逻辑可能无法完全模拟实盘的资金分配,建议先用单账号回测逻辑验证策略,再切换到实盘模式使用多账号代码。