🚀 AI 一键生成 qmt 策略代码

详解 cancel_task:如何通过代码撤销智能算法任务?

qmt | 发布时间: 2025/12/23
以下内容由 EasyQuant 生成。

问题描述

怎么用代码撤销正在运行的算法交易任务?

解决方案

在 QMT 中,撤销正在运行的算法交易任务(如智能算法 VWAP、TWAP 或手动设置的算法单)主要使用 cancel_task 函数。

通常有两种撤销方式:

  1. 撤销指定任务:先获取任务列表,找到特定任务的 ID (taskId),然后撤销。
  2. 撤销全部任务:直接调用函数并传入空字符串作为任务 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("-> 全部撤销指令发送失败")

代码解析

  1. get_trade_detail_data(..., 'task'):
    这是获取任务详情的关键。不同于获取普通的委托(Order)或成交(Deal),这里传入 'task' 参数专门用于获取算法交易产生的父任务对象。
  2. task 对象属性:
    • m_nTaskId: 这是 cancel_task 需要的唯一标识符。
    • m_eStatus: 任务状态。虽然代码中直接尝试撤销,但在复杂逻辑中,您可能需要判断状态(例如只撤销“暂停”或“运行中”的任务)。
  3. cancel_task:
    这是执行动作的函数。它不会阻塞等待撤销成功,而是发送撤销指令。撤销结果可以通过交易回报回调(如 order_callback 或查询任务状态变化)来确认。

常见问题 Q&A

Q: cancel_taskcancel 有什么区别?
A: cancel_task 用于撤销算法任务(父任务),例如一个拆单策略,撤销任务会停止后续的拆单行为并撤销当前挂单。cancel 用于撤销具体的普通委托(子单),即具体的某一笔挂单。如果是算法交易,建议使用 cancel_task

Q: 为什么调用了撤销,任务还在列表中?
A: get_trade_detail_data 获取的是快照数据。撤销是一个异步过程,发送指令后,交易所或柜台处理需要时间。任务状态更新后(变为“已撤销”),它仍然会存在于列表中(作为历史记录),直到当天收盘或重启客户端。您需要通过 m_eStatus 属性来判断它是否已经停止。

Q: 如何暂停而不是撤销任务?
A: 如果只想暂时停止算法运行但保留进度,可以使用 pause_task(taskId, accountId, accountType, ContextInfo) 函数。恢复时使用 resume_task