问题描述
解决方案
在 QMT 中,撤销正在运行的算法交易任务(如智能算法 VWAP、TWAP 或手动设置的算法单)主要使用 cancel_task 函数。
通常有两种撤销方式:
- 撤销指定任务:先获取任务列表,找到特定任务的 ID (
taskId),然后撤销。 - 撤销全部任务:直接调用函数并传入空字符串作为任务 ID,即可撤销该账号下所有可撤销的任务。
核心函数说明
cancel_task(taskId, accountId, accountType, ContextInfo)
- taskId (string): 任务编号。如果传入空字符串
'',表示撤销该资金账号下所有可撤销的任务。 - accountId (string): 资金账号。
- accountType (string): 账号类型(如
'STOCK','FUTURE')。 - ContextInfo: 全局对象。
完整代码示例
以下代码展示了如何在策略中获取当前正在运行的任务,并将其逐一撤销。同时也包含了一键撤销所有任务的方法。
# -*- coding: gbk -*-
def init(ContextInfo):
# 设置资金账号
# 请将下面的账号替换为您实际的资金账号
ContextInfo.accID = '6000000000'
ContextInfo.accType = 'STOCK' # 账号类型:STOCK股票,FUTURE期货
ContextInfo.set_account(ContextInfo.accID)
print("策略初始化完成,账号已设定。")
def handlebar(ContextInfo):
# 仅在实时行情的最后一根K线运行,避免历史回测重复触发
if not ContextInfo.is_last_bar():
return
# 示例触发条件:这里为了演示,直接调用撤销逻辑
# 在实际策略中,您可以根据特定逻辑(如时间到了、行情反转等)来调用
# 方式一:查询并撤销特定状态的任务(推荐,更可控)
cancel_specific_tasks(ContextInfo)
# 方式二:暴力撤销该账号下所有任务(慎用)
# cancel_all_tasks(ContextInfo)
def cancel_specific_tasks(ContextInfo):
"""
获取任务列表并撤销正在运行的任务
"""
# 获取当前账号的所有任务数据
# 参数 'task' 表示获取任务层级的数据
task_list = get_trade_detail_data(ContextInfo.accID, ContextInfo.accType, 'task')
if not task_list:
print("当前没有查询到任何算法任务。")
return
print(f"查询到 {len(task_list)} 个任务,开始检查状态...")
for task in task_list:
# task.m_nTaskId: 任务ID
# task.m_eStatus: 任务状态
# task.m_strMsg: 状态描述信息
# 打印任务信息
print(f"任务ID: {task.m_nTaskId}, 状态: {task.m_eStatus}, 说明: {task.m_strMsg}")
# 判断任务是否处于可撤销状态
# 通常 23=运行中(TASK_STATUS_RUNNING), 21=等待中(TASK_STATUS_WAITING) 等状态需要撤销
# 这里简单判断,只要不是已完成(26)或已撤销(27)等终态,都尝试撤销
# 注意:具体状态码对应值请参考文档附录,或直接尝试撤销,API会自动判断是否可撤
# 执行撤销
ret = cancel_task(task.m_nTaskId, ContextInfo.accID, ContextInfo.accType, ContextInfo)
if ret:
print(f"-> 已发送撤销指令:任务ID {task.m_nTaskId}")
else:
print(f"-> 撤销指令发送失败或任务不可撤:任务ID {task.m_nTaskId}")
def cancel_all_tasks(ContextInfo):
"""
一键撤销该账号下所有可撤销的任务
"""
print("正在尝试撤销所有任务...")
# taskId 传入空字符串 '' 代表撤销所有
ret = cancel_task('', ContextInfo.accID, ContextInfo.accType, ContextInfo)
if ret:
print("-> 已发送【全部撤销】指令")
else:
print("-> 全部撤销指令发送失败")
代码解析
get_trade_detail_data(..., 'task'):
这是获取任务详情的关键。不同于获取普通的委托(Order)或成交(Deal),这里传入'task'参数专门用于获取算法交易产生的父任务对象。task对象属性:m_nTaskId: 这是cancel_task需要的唯一标识符。m_eStatus: 任务状态。虽然代码中直接尝试撤销,但在复杂逻辑中,您可能需要判断状态(例如只撤销“暂停”或“运行中”的任务)。
cancel_task:
这是执行动作的函数。它不会阻塞等待撤销成功,而是发送撤销指令。撤销结果可以通过交易回报回调(如order_callback或查询任务状态变化)来确认。
常见问题 Q&A
Q: cancel_task 和 cancel 有什么区别?
A: cancel_task 用于撤销算法任务(父任务),例如一个拆单策略,撤销任务会停止后续的拆单行为并撤销当前挂单。cancel 用于撤销具体的普通委托(子单),即具体的某一笔挂单。如果是算法交易,建议使用 cancel_task。
Q: 为什么调用了撤销,任务还在列表中?
A: get_trade_detail_data 获取的是快照数据。撤销是一个异步过程,发送指令后,交易所或柜台处理需要时间。任务状态更新后(变为“已撤销”),它仍然会存在于列表中(作为历史记录),直到当天收盘或重启客户端。您需要通过 m_eStatus 属性来判断它是否已经停止。
Q: 如何暂停而不是撤销任务?
A: 如果只想暂时停止算法运行但保留进度,可以使用 pause_task(taskId, accountId, accountType, ContextInfo) 函数。恢复时使用 resume_task。