问题描述
解决方案
在 QMT 中,要实现持仓发生变化时立刻得到通知并执行操作,最直接且高效的方法是使用 交易回报实时主推函数 中的 position_callback。
这是一种事件驱动的机制。与在 handlebar 中轮询查询持仓不同,position_callback 会在交易柜台推送持仓变动信息(如成交导致持仓变化、出入金导致资产变化等)时,由系统自动触发调用。
实现步骤
- 绑定账号:在
init函数中必须调用ContextInfo.set_account(account_id),否则无法接收到该账号的主推消息。 - 定义回调函数:在策略代码中定义
def position_callback(ContextInfo, positionInfo):。 - 编写逻辑:在回调函数内部,通过
positionInfo对象获取具体的持仓信息,并编写相应的下单或风控逻辑。
策略代码示例
以下是一个完整的示例代码。当持仓发生变化时,它会打印变动的股票代码和当前持仓量。你可以在注释的位置添加具体的交易逻辑(如止盈止损、对冲下单等)。
# -*- coding: gbk -*-
def init(ContextInfo):
# 1. 设置资金账号 (必须步骤)
# 请将 'YOUR_ACCOUNT_ID' 替换为您实际的资金账号
# 如果是股票账号通常是数字,如果是期货等其他账号请参考QMT显示的ID
account_id = '6000000000'
ContextInfo.set_account(account_id)
print(f"策略初始化完成,已绑定账号: {account_id},等待持仓变动推送...")
def handlebar(ContextInfo):
# 必须存在该函数,保持策略运行,但此处不需要写轮询逻辑
pass
def position_callback(ContextInfo, positionInfo):
"""
持仓变动回调函数
当持仓状态发生变化时(如成交、数据刷新),系统会自动调用此函数
:param ContextInfo: 全局上下文对象
:param positionInfo: 持仓对象,包含具体的持仓信息
"""
# 2. 获取持仓关键信息
# 证券代码 (如 '600000.SH')
stock_code = positionInfo.m_strInstrumentID
# 证券名称
stock_name = positionInfo.m_strInstrumentName
# 当前总持仓量
volume = positionInfo.m_nVolume
# 可用持仓量
can_use_volume = positionInfo.m_nCanUseVolume
# 持仓成本
open_price = positionInfo.m_dOpenPrice
# 持仓盈亏
profit = positionInfo.m_dPositionProfit
# 账号ID
acc_id = positionInfo.m_strAccountID
# 3. 打印日志或执行逻辑
print(f"【持仓变动通知】 账号:{acc_id} | 标的:{stock_name}({stock_code})")
print(f" -> 当前持仓: {volume}, 可用: {can_use_volume}, 盈亏: {profit}")
# 4. 在此处编写您的业务逻辑
# 例如:如果持仓量变为0(已清仓),则执行某些清理操作
if volume == 0:
print(f" -> 检测到 {stock_code} 已清仓,执行后续逻辑...")
# do_something()
# 例如:如果持仓盈亏超过一定比例,触发止盈单
# if profit > 10000:
# passorder(...)
关键点说明
-
positionInfo对象属性:
回调函数传入的positionInfo是一个对象,包含了该只股票的详细持仓状态。常用的属性包括:m_strInstrumentID: 证券代码m_nVolume: 当前持仓数量m_nCanUseVolume: 可用股份数量m_dPositionProfit: 持仓盈亏m_dOpenPrice: 持仓成本m_dMarketValue: 最新市值- (更多属性请参考 QMT API 文档附录 6.4.4 position 持仓对象)
-
运行模式:
此功能仅在 “实盘” 或 “模拟交易” 模式下有效。在回测模式中,由于没有真实的柜台推送机制,通常不会触发此回调(回测主要依赖handlebar的逐 K 线撮合)。 -
触发频率:
position_callback可能会被频繁触发。例如,一笔委托分多次成交,或者行情刷新导致持仓市值变化,都可能触发推送。因此,在编写交易逻辑(如passorder)时,建议加上状态判断或锁机制,防止针对同一变动重复下单。
Q&A
Q: position_callback 和 handlebar 有什么区别?
A: handlebar 是由行情驱动的,每来一个 Tick 或每根 K 线结束时运行一次,适合做基于行情的策略计算。position_callback 是由交易柜台驱动的,只有当账号的持仓数据发生变化(如成交、资金变动)时才会触发,适合做交易风控、止盈止损监控或多策略协同。
Q: 如果我有多个账号,这个函数能区分吗?
A: 可以。positionInfo.m_strAccountID 属性存储了发生变动的账号 ID。你可以在函数内部通过 if positionInfo.m_strAccountID == '账号A': 来区分处理。
Q: 为什么我设置了代码但没有收到推送?
A: 请检查以下几点:
- 确保在
init中正确调用了ContextInfo.set_account()。 - 确保策略处于“实盘”或“模拟”运行状态,且账号已登录成功。
- 确保确实发生了持仓变动(如果没有交易或数据刷新,不会无故触发)。