🚀 AI 一键生成 ptrade 策略代码

PTrade 策略中如何利用 on_order_response 事件实现低延迟交易逻辑?

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

问题描述

如何根据订单的实时状态变化来执行下一步操作?

解决方案

在PTrade中,要根据订单的实时状态变化(如“已成”、“废单”、“已撤”等)来执行下一步操作,最推荐的方式是使用 委托主推回调函数 on_order_response

相比于在 handle_data 中轮询查询订单状态,on_order_response 是由交易柜台或系统在订单状态发生变更时主动触发的,具有响应速度快实时性强的特点。

核心机制

  1. on_order_response(context, order_list): 当委托单状态发生变化时(如变为已报、部成、已成、废单等),系统会自动调用此函数。
  2. order_list: 这是一个列表,包含发生变化的订单信息字典。
  3. status 字段: 字典中的 status 字段代表当前订单的状态(例如:'8'代表全部成交,'9'代表废单)。

策略逻辑示例

下面的策略展示了如何实现以下逻辑:

  1. 在盘中下单买入股票。
  2. 通过 on_order_response 监听订单状态。
  3. 如果订单全部成交(Status='8'),则立即执行下一步操作(例如:打印日志或下止盈单)。
  4. 如果订单废单(Status='9'),则记录错误信息。

PTrade 策略代码

def initialize(context):
    # 初始化策略
    g.security = '600570.SS' # 恒生电子
    set_universe(g.security)
    
    # 设置一个标志位,防止handle_data重复下单
    g.order_sent = False

def before_trading_start(context, data):
    # 每日盘前重置标志位
    g.order_sent = False

def handle_data(context, data):
    # 示例:在盘中简单下单
    if not g.order_sent:
        # 下单买入100股,限价40元(仅为示例,请根据行情调整)
        # 注意:这里我们记录了返回的 order_id,虽然在回调中主要通过 entrust_no 或 order_id 匹配
        g.current_order_id = order(g.security, 100, limit_price=40)
        
        if g.current_order_id:
            log.info("已发送买入委托,订单ID: %s" % g.current_order_id)
            g.order_sent = True

def on_order_response(context, order_list):
    """
    委托主推回调函数
    当订单状态发生变化时,本函数会被触发
    """
    for order_info in order_list:
        # 获取订单的关键信息
        status = order_info.get('status')
        entrust_no = order_info.get('entrust_no')
        stock_code = order_info.get('stock_code')
        order_id = order_info.get('order_id')
        
        log.info("收到订单更新 - 代码: %s, 委托编号: %s, 状态: %s" % (stock_code, entrust_no, status))
        
        # 根据状态执行下一步操作
        # 状态 '8' 代表 "已成" (全部成交)
        if status == '8':
            log.info("【策略触发】订单已全部成交!准备执行下一步操作...")
            # --- 在这里写成交后的逻辑 ---
            # 例如:记录成交,或者挂出止损单
            # 注意:如果在回调中再次下单,务必做好逻辑控制,防止死循环
            do_next_step(context, stock_code)
            
        # 状态 '9' 代表 "废单"
        elif status == '9':
            error_info = order_info.get('error_info', '未知错误')
            log.error("【策略警告】订单废单!原因: %s" % error_info)
            # --- 在这里写废单后的逻辑 ---
            # 例如:重置标志位允许重新下单
            g.order_sent = False
            
        # 状态 '6' 代表 "已撤"
        elif status == '6':
            log.info("【策略提示】订单已撤销。")
            g.order_sent = False

def do_next_step(context, stock_code):
    """
    自定义的下一步操作函数
    """
    log.info("正在为 %s 执行后续逻辑(如:更新持仓记录、发送通知等)" % stock_code)
    # 示例:获取当前持仓
    pos = get_position(stock_code)
    log.info("当前持仓数量: %s" % pos.amount)

关键状态码说明 (Data Dictionary)

on_order_response 中,status 字段是判断的核心依据。根据 PTrade 文档,常用状态码如下:

状态码 含义 说明
2 已报 订单已报送至交易所,等待撮合
4 部成待撤 部分成交,剩余部分已申请撤单
5 部撤 部分成交,剩余部分已撤单
6 已撤 订单已全部撤销
7 部成 部分成交,剩余部分仍在挂单
8 已成 全部成交 (最常用的判断状态)
9 废单 订单被柜台或交易所拒绝 (需检查价格、资金等)

注意事项

  1. 死循环风险
    如果在 on_order_response 中直接调用 order 系列函数进行下单,而新下的单又触发了状态变化(如废单),可能会导致无限递归调用。务必在回调中加入逻辑判断(如检查持仓、设置特定的全局Flag)来避免死循环。

  2. 适用环境
    on_order_response 主要用于**交易(实盘/模拟盘)**环境。在回测环境中,虽然也支持该回调,但回测引擎的撮合机制是基于K线或Tick模拟的,状态变化可能不如实盘那样丰富(例如回测中很少出现“已报”这种中间状态,通常直接跳到“已成”)。

  3. 数据类型
    order_list 中的数据是字典(dict),且部分字段(如 amount, price)可能是浮点数或整数,而 status 是字符串。比较状态时请使用字符串 '8' 而不是数字 8

  4. 成交主推 (on_trade_response)
    如果你更关注成交细节(如具体的成交价格、成交时间、分笔成交情况),可以使用 on_trade_response。但如果你只关心订单的最终生命周期(是否这就完了、是否废了),on_order_response 更为宏观和直接。


Q&A

Q: on_order_responseget_orders() 有什么区别?
A: on_order_response事件驱动的,只有当状态变了才会触发,实时性极高,适合高频或对时效要求高的策略。get_orders()主动查询,通常在 handle_data 中轮询使用,适合低频策略或做定期状态检查。

Q: 为什么我在回测中收不到 '2' (已报) 的状态?
A: 回测引擎通常为了效率会简化撮合过程。如果你的订单满足撮合条件,回测引擎可能直接将其状态置为 '8' (已成),跳过了中间的申报过程。

Q: 如果我想在部分成交(部成)时也执行操作怎么办?
A: 可以在 on_order_response 中监听 status 为 '7' (部成) 的情况。但要注意,一个订单可能会多次触发“部成”状态(每次有新成交时),或者同时触发 on_trade_response。建议结合 filled_amount(已成交数量)来判断进度。