🚀 AI 一键生成 ptrade 策略代码

ptrade撤单时成交主推(on_trade_response)返回数量异常怎么办?

ptrade | 发布时间: 2026/5/20
以下内容由 EasyQuant 生成。

问题描述

对某一笔订单做撤单时,撤成状态的成交主推只返回了100的委托数量,实际撤了1100股?

客户是使用量化策略进行委托的,检查量化后台sys日志,发现确实没有1000委托数量的部撤记录,检查PTRADE_SA的msgc插件日志,发现对应的主推和量化sys日志中打印的是一致的

解决方案

问题分析

在 ptrade 量化交易平台中,当您对某一笔订单进行撤单操作时,如果发现 on_trade_response(成交主推)返回的撤单数量(business_amount)与实际撤单数量不符(例如实际撤了1100股,但主推只显示100股),且底层日志(sys日志和msgc插件日志)也一致,通常是由以下几个原因导致的:

  1. 柜台推送机制差异:主推数据是由底层券商柜台直接推送的。在某些极端情况下(如行情剧烈波动、撮合引擎分批处理),柜台可能会将一笔撤单拆分成多次推送,或者在推送时发生了合并/遗漏。如果底层日志确实没有那1000股的推送记录,说明柜台本身就没有将这部分数据推送上来
  2. 订单状态(Status)的理解偏差:在撤单时,需要特别关注主推字典中的 status 字段。如果是 5(部撤),说明订单部分成交、部分撤销;如果是 6(已撤),说明订单完全撤销。有时主推中的 business_amount 可能只代表某一次撮合动作涉及的数量,而不是总撤单量。
  3. 分批成交与撤单的并发:如果在您发起撤单的瞬间,订单刚好发生了部分成交(例如成交了1000股),那么实际成功撤销的可能只有剩下的100股。此时主推返回撤单100股是正确的,您需要检查是否有对应的1000股的成交主推

排查步骤与解决方案

针对这种主推数据可能存在遗漏或分批的情况,建议在编写量化策略时,不要完全依赖单次主推中的 business_amount 来计算最终的持仓或订单状态,而应采用“主推触发 + 主动查询”的防御性编程方式。

1. 检查历史成交记录

首先,确认这笔订单是否在撤单前或撤单瞬间发生了部分成交。您可以通过 get_trades() 或检查之前的 on_trade_response 记录,看看是否有1000股的成交记录。

2. 结合 get_order() 获取最终准确状态

由于主推可能存在延迟或分批,最稳妥的做法是在收到撤单主推后,使用 get_order(order_id) 主动查询该订单的最新全貌。

Order 对象中包含了:

  • amount: 初始委托总数量
  • filled: 已成交数量
  • status: 当前最新状态

实际撤单数量 = 初始委托数量 (amount) - 已成交数量 (filled)

3. 策略代码优化示例

以下是一个稳健处理撤单主推的代码示例:

def initialize(context):
    g.security = '600570.SS'
    set_universe(g.security)
    g.target_order_id = None

def on_trade_response(context, trade_list):
    for trade in trade_list:
        # 检查是否是撤单相关的状态:'5' (部撤) 或 '6' (已撤)
        if trade.get('status') in ['5', '6']:
            order_id = trade.get('order_id')
            if not order_id:
                continue
            
            # 1. 获取主推中的数量(可能不完整)
            pushed_cancel_amount = trade.get('business_amount', 0)
            log.info(f"收到撤单主推,订单号: {order_id}, 主推撤单数量: {pushed_cancel_amount}")
            
            # 2. 主动查询订单最新状态,计算真实的撤单数量
            order_info_list = get_order(order_id)
            if order_info_list and len(order_info_list) > 0:
                order_obj = order_info_list[0]
                total_amount = abs(order_obj.amount) # 委托总数(取绝对值兼容买卖)
                filled_amount = abs(order_obj.filled) # 已成交总数
                
                # 真实撤单数量 = 委托总数 - 已成交总数
                actual_canceled_amount = total_amount - filled_amount
                
                log.info(f"订单 {order_id} 最终状态: {order_obj.status}, 委托总数: {total_amount}, 已成交: {filled_amount}, 实际撤单总数: {actual_canceled_amount}")
                
                if pushed_cancel_amount != actual_canceled_amount:
                    log.warning(f"注意:主推撤单数量 ({pushed_cancel_amount}) 与 实际计算撤单总数 ({actual_canceled_amount}) 不一致!请以实际计算为准。")

def handle_data(context, data):
    # 示例:下单并在条件满足时撤单
    pass

总结

如果底层日志确认柜台只推送了100股的撤单记录,这属于柜台推送层面的现象(可能是分批推送丢失,或者是并发成交导致)。作为策略开发者,最佳实践是在 on_trade_response 中仅将主推作为“状态发生变化”的触发信号,随后调用 get_order() 获取订单的 amountfilled 字段,通过计算得出准确的撤单数量,从而保证策略逻辑的严密性。