3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 QMT 中,区分和追踪不同开仓逻辑触发的委托,最直接且有效的方法是利用下单函数 passorder 中的 userOrderId 参数。
passorder 函数时,将具体的逻辑名称(如 "MACD_Open" 或 "Boll_Breakout")赋值给 userOrderId 参数。order_callback)、成交回报(deal_callback)或查询交易明细(get_trade_detail_data)时,读取返回对象中的 m_strRemark 属性。QMT 底层会将 userOrderId 的内容透传到 m_strRemark 中。以下代码展示了如何在一个策略中定义两种不同的开仓逻辑(均线策略和随机入场),并通过 userOrderId 进行区分,同时在回调函数中打印追踪。
# -*- coding: gbk -*-
import random
def init(ContextInfo):
# 设置资金账号
ContextInfo.accid = '6000000000' # 请替换为您的真实资金账号
ContextInfo.set_account(ContextInfo.accid)
# 定义股票池
ContextInfo.stock = '600000.SH'
ContextInfo.set_universe([ContextInfo.stock])
# 策略运行参数
ContextInfo.strategy_name = "MultiLogicStrategy"
def handlebar(ContextInfo):
# 获取当前K线位置
index = ContextInfo.barpos
# 获取当前时间
timetag = ContextInfo.get_bar_timetag(index)
# 跳过历史K线,只在最新K线或实时行情运行(模拟实盘场景)
if not ContextInfo.is_last_bar():
return
# --- 逻辑 A: 均线策略 (示例) ---
# 获取收盘价
close_data = ContextInfo.get_market_data(['close'], stock_code=[ContextInfo.stock], count=21, period='1d')
if close_data is not None and not close_data.empty:
closes = close_data['close']
if len(closes) >= 20:
ma5 = closes[-5:].mean()
ma20 = closes[-20:].mean()
current_price = closes[-1]
# 假设金叉开仓
if ma5 > ma20:
# 【关键点】在这里定义逻辑标签
logic_tag = "Logic_MA_Cross"
print(f"触发 {logic_tag} 开仓信号")
passorder(
23, # opType: 23-买入
1101, # orderType: 1101-单股单账号
ContextInfo.accid, # accountId
ContextInfo.stock, # orderCode
5, # prType: 5-最新价
-1, # price: 最新价模式下无效
100, # volume: 100股
ContextInfo.strategy_name, # strategyName
1, # quickTrade: 1-立即下单
logic_tag, # userOrderId: 【这里传入逻辑标签】
ContextInfo # ContextInfo
)
# --- 逻辑 B: 随机策略 (示例) ---
# 仅作演示,模拟另一种完全不同的触发条件
rand_val = random.random()
if rand_val > 0.95:
# 【关键点】定义不同的逻辑标签
logic_tag = "Logic_Random_Entry"
print(f"触发 {logic_tag} 开仓信号")
passorder(
23,
1101,
ContextInfo.accid,
ContextInfo.stock,
5,
-1,
100,
ContextInfo.strategy_name,
1,
logic_tag, # userOrderId: 【传入不同的标签】
ContextInfo
)
# 委托回报回调:追踪下单结果
def order_callback(ContextInfo, orderInfo):
# orderInfo 是委托对象
# m_strRemark 对应下单时的 userOrderId
source_logic = orderInfo.m_strRemark
print("=" * 30)
print(f"收到委托回报")
print(f"委托编号: {orderInfo.m_strOrderSysID}")
print(f"证券代码: {orderInfo.m_strInstrumentID}")
print(f"触发逻辑(Remark): {source_logic}") # 这里会打印 "Logic_MA_Cross" 或 "Logic_Random_Entry"
print(f"委托状态: {orderInfo.m_nOrderStatus}")
print("=" * 30)
# 可以在这里根据不同的 source_logic 做进一步处理
if source_logic == "Logic_MA_Cross":
# 记录均线策略的委托状态...
pass
elif source_logic == "Logic_Random_Entry":
# 记录随机策略的委托状态...
pass
# 成交回报回调
def deal_callback(ContextInfo, dealInfo):
# dealInfo 是成交对象
# m_strRemark 同样对应下单时的 userOrderId
source_logic = dealInfo.m_strRemark
print("=" * 30)
print(f"收到成交回报")
print(f"成交编号: {dealInfo.m_strTradeID}")
print(f"触发逻辑(Remark): {source_logic}")
print(f"成交价格: {dealInfo.m_dPrice}")
print("=" * 30)
在 passorder 函数中,参数的顺序是固定的(除非使用关键字参数,但 QMT 文档通常推荐位置参数)。userOrderId 通常位于第 10 个参数位置(视具体重载而定,但在 Python API 中通常如下):
passorder(
opType,
orderType,
accountid,
orderCode,
prType,
modelprice,
volume,
strategyName, # 策略名称(用于区分不同策略文件)
quickTrade, # 快速交易标志(通常设为1或2)
userOrderId, # 【核心】用户自定义ID,即备注信息
ContextInfo
)
order_callback 和 deal_callback 返回的对象中,这个属性存储的就是你下单时传入的 userOrderId。如果你需要在盘后分析或在 handlebar 中查询之前的委托是哪个逻辑触发的,可以使用 get_trade_detail_data:
def check_orders(ContextInfo):
# 获取当天的所有委托
orders = get_trade_detail_data(ContextInfo.accid, 'stock', 'order')
for order in orders:
# 读取备注
logic = order.m_strRemark
status = order.m_nOrderStatus
if logic == "Logic_MA_Cross":
print(f"发现均线策略委托,状态: {status}")
elif logic == "Logic_Random_Entry":
print(f"发现随机策略委托,状态: {status}")
Q: userOrderId 有长度限制吗?
A: 是的,通常建议不要过长,保持在简短的字符串(如 32 个字符以内)是安全的,例如 "MA_Buy" 或 "RSI_Sell"。
Q: 如果我不填 strategyName 和 quickTrade,直接传 userOrderId 可以吗?
A: 不可以。因为 Python 的位置参数机制,如果你想传第 10 个参数,必须把第 8 和第 9 个参数填上。通常 strategyName 填策略名或空字符串,quickTrade 填 1(立即下单)或 0。
Q: 这个方法在回测中有效吗?
A: 在回测模式下,passorder 的 userOrderId 参数通常会被忽略或不完全支持回调中的透传,因为回测主要关注净值计算。这个功能主要用于实盘和模拟盘的信号追踪与风控。
Q: 可以在 userOrderId 中传递复杂信息吗?
A: 可以传递 JSON 字符串,例如 "{'logic':'MA', 'id':101}",然后在回调中解析这个字符串,从而携带更多元数据。