🚀 AI 一键生成 ptrade 策略代码

PTrade API 教程:如何精准判断并处理每一种订单状态?

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

问题描述

如何实时跟踪订单的完整状态变化,例如从下单到部分成交再到完全成交?

解决方案

在PTrade中,要实时跟踪订单的完整状态变化(如下单、部分成交、完全成交),最推荐且最高效的方式是使用 事件驱动的回调函数

PTrade 提供了两个关键的回调接口:

  1. on_order_response(context, order_list)委托主推。当订单状态发生变化(如变为“已报”、“部成”、“已成”、“废单”等)时,该函数会被立即触发。
  2. on_trade_response(context, trade_list)成交主推。当订单发生具体的成交动作(即有一笔撮合成功)时,该函数会被触发。

相比于在 handle_data 中轮询 get_order(),使用这两个回调函数能以更低的延迟获取状态更新。

核心状态码说明

在处理回调数据时,需要关注 status 字段:

  • "2": 已报 (Submitted)
  • "7": 部成 (Partial Filled) - 此时订单还有剩余数量未成交
  • "8": 已成 (Filled) - 订单全部成交
  • "9": 废单 (Rejected)
  • "6": 已撤 (Canceled)
  • "5": 部撤 (Partial Canceled)

策略代码示例

以下代码展示了如何下单,并通过回调函数实时打印订单从“已报”到“成交”的全过程日志。

def initialize(context):
    """
    初始化函数
    """
    # 设置股票池
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 用于记录我们关注的订单ID
    g.my_order_id = None
    # 标记是否已下单,防止重复下单
    g.has_ordered = False

def handle_data(context, data):
    """
    盘中运行函数
    """
    # 仅为了演示:如果还没下单,且有行情,则下一个限价单
    if not g.has_ordered:
        # 获取当前价格
        current_price = data[g.security]['close']
        
        # 下单买入 1000 股,使用限价单(Limit Order)
        # 注意:为了演示部分成交或成交过程,这里以当前价下单
        g.my_order_id = order(g.security, 1000, limit_price=current_price)
        
        if g.my_order_id:
            log.info("【策略下单】下单成功,订单ID: %s,等待回调更新状态..." % g.my_order_id)
            g.has_ordered = True

def on_order_response(context, order_list):
    """
    委托主推回调:当订单状态发生变化时触发(如:已报、部成、已成、撤单等)
    """
    for order_info in order_list:
        # 过滤出我们自己下的那个订单
        if order_info['order_id'] == g.my_order_id:
            status = order_info['status']
            entrust_no = order_info['entrust_no']
            
            # 解析状态
            status_desc = "未知状态"
            if status == '2':
                status_desc = "已报 (等待成交)"
            elif status == '7':
                status_desc = "部成 (部分成交,剩余待成交)"
            elif status == '8':
                status_desc = "已成 (全部成交)"
            elif status == '9':
                status_desc = "废单"
            elif status == '6':
                status_desc = "已撤"
            elif status == '5':
                status_desc = "部撤"
            
            log.info("【委托状态更新】订单ID: %s, 委托编号: %s, 当前状态: %s (%s), 已成交数量: %s" % (
                g.my_order_id, 
                entrust_no, 
                status, 
                status_desc, 
                order_info['business_amount']
            ))
            
            # 如果完全成交,可以在这里做后续逻辑处理
            if status == '8':
                log.info(">>> 订单 %s 已全部完成,任务结束。" % g.my_order_id)

def on_trade_response(context, trade_list):
    """
    成交主推回调:当发生具体成交动作时触发(比on_order_response更细节,关注每一笔撮合)
    """
    for trade_info in trade_list:
        # 过滤出我们自己下的那个订单
        if trade_info['order_id'] == g.my_order_id:
            log.info("【成交明细推送】订单ID: %s 发生一笔成交!成交数量: %s, 成交价格: %s, 成交金额: %s" % (
                g.my_order_id,
                trade_info['business_amount'],
                trade_info['business_price'],
                trade_info['business_balance']
            ))

代码解析

  1. 下单记录:在 handle_data 中下单后,我们将返回的 order_id 保存在全局变量 g.my_order_id 中,以便后续在回调中筛选。
  2. on_order_response (委托主推)
    • 这是跟踪宏观状态的最佳位置。
    • 当订单刚报到柜台时,状态变为 '2' (已报)。
    • 当撮合了一部分但未全部完成时,状态变为 '7' (部成)。
    • 当全部撮合完成时,状态变为 '8' (已成)。
    • 我们通过判断 status 字段来打印对应的日志。
  3. on_trade_response (成交主推)
    • 这是跟踪微观成交的最佳位置。
    • 如果一个 1000 股的订单分成了两笔 500 股成交,这个函数会被调用两次。
    • 它提供了具体的成交价格和成交量,适合用于精确计算均价或记录流水。

注意事项

  • 回测 vs 实盘:在回测模式下,由于撮合机制是模拟的,通常订单会瞬间从“已报”变为“已成”,可能看不到“部成”的过程(除非成交量受限)。在实盘或仿真交易中,这些状态变化会非常清晰。
  • 数据类型:回调函数返回的 order_listtrade_list 中的元素是字典 (dict) 类型,而不是对象,因此访问属性时必须使用 ['key'] 的方式(如 order_info['status']),而不是 .status
  • 速度:这两个回调函数的响应速度快于 get_order() 查询,适合高频或对时效性要求高的策略。

Q&A

Q: 为什么我在回测里看不到“部成”状态?
A: 回测引擎通常基于分钟线或日线撮合,假设流动性充足。除非您设置了严格的成交量限制(如 set_volume_ratio),否则小额订单通常会一次性完全成交,直接从状态 '2' 跳到 '8'。

Q: on_order_responseget_order() 有什么区别?
A: get_order() 是主动查询(轮询),通常在 handle_data 中调用,受限于策略运行频率(如每分钟一次)。on_order_response 是被动接收(推送),一旦柜台状态变化立即触发,延迟更低,且能捕获到中间状态。

Q: 如果我不使用回调,怎么查询订单状态?
A: 您可以在 handle_data 中使用 curr_order = get_order(g.my_order_id),然后检查 curr_order.status。但这只能获取调用那一刻的状态,可能会漏掉中间快速变化的过程。