🚀 AI 一键生成 ptrade 策略代码

PTrade 低延迟交易核心:如何利用委托回报主推 (on_order_response) 实现事件驱动?

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

问题描述

如何在下单委托成功或者成交后立刻执行下一步操作?

解决方案

在PTrade中,若要在下单委托成功(报单成功)或成交后立刻执行下一步操作,最推荐且响应速度最快的方法是使用交易主推回调函数

相比于在 handle_datatick_data 中轮询查询订单状态,回调函数是事件驱动的,即当柜台返回委托状态变化或成交回报时,系统会自动触发这些函数。

核心解决方案

  1. 委托成功(已报):使用 on_order_response(context, order_list)
  2. 成交成功:使用 on_trade_response(context, trade_list)

注意:这两个函数仅在交易模式(实盘或仿真交易)下可用,回测模式下通常不支持或行为不同。


代码实现示例

以下策略展示了如何定义这两个函数,并在收到回报时立即打印日志或执行逻辑。

def initialize(context):
    # 初始化股票池
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 设置一个标志位,防止handle_data重复下单,仅作演示用
    g.order_placed = False

def handle_data(context, data):
    # 示例:在盘中下一个买单
    if not g.order_placed:
        log.info("开始下单...")
        # 下单买入100股
        order(g.security, 100)
        g.order_placed = True

# ---------------------------------------------------------
# 1. 委托主推回调:当订单状态发生变化时(如:已报、废单、已撤等)触发
# ---------------------------------------------------------
def on_order_response(context, order_list):
    """
    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')
        
        # 状态 '2' 代表 "已报" (委托成功到达柜台)
        if status == '2':
            log.info("【委托成功回调】股票: %s, 委托编号: %s, 状态: 已报" % (stock_code, entrust_no))
            
            # --- 在此处编写委托成功后立刻要执行的逻辑 ---
            # 例如:记录日志、更新自定义变量等
            # 注意:尽量避免在此处直接再次下单,除非有严格的逻辑控制,否则容易造成死循环
            
        # 状态 '9' 代表 "废单"
        elif status == '9':
            log.warning("【废单回调】股票: %s, 原因: %s" % (stock_code, order_info.get('error_info')))

# ---------------------------------------------------------
# 2. 成交主推回调:当订单发生生成交时触发
# ---------------------------------------------------------
def on_trade_response(context, trade_list):
    """
    trade_list 是一个列表,包含最新的成交记录字典
    """
    for trade_info in trade_list:
        stock_code = trade_info.get('stock_code')
        amount = trade_info.get('business_amount') # 成交数量
        price = trade_info.get('business_price')   # 成交价格
        direction = trade_info.get('entrust_bs')   # 1-买,2-卖
        
        direction_str = "买入" if direction == '1' else "卖出"
        
        log.info("【成交回调】股票: %s, 方向: %s, 数量: %s, 价格: %s" % (stock_code, direction_str, amount, price))
        
        # --- 在此处编写成交后立刻要执行的逻辑 ---
        # 例如:
        # 1. 立即对冲(如期现套利)
        # 2. 止盈止损单挂单
        # 3. 策略仓位更新
        
        # 示例:买入成交后,立即挂一个高价卖单(网格交易常用逻辑)
        if direction == '1': # 如果是买入成交
            target_price = price * 1.02 # 目标利润2%
            # 注意:在回调中下单务必谨慎,防止逻辑错误导致无限下单
            # order(stock_code, -amount, limit_price=target_price) 
            log.info("计划卖出价格: %s" % target_price)

关键点解析

1. on_order_response (委托主推)

  • 触发时机:订单状态发生任何改变时(变为已报、部成、已成、已撤、废单等)。
  • 判断委托成功:检查 status 字段是否为 '2'
  • 数据结构order_list 中的元素包含 entrust_no (委托编号), status (状态), order_id (PTrade内部ID) 等。

2. on_trade_response (成交主推)

  • 触发时机:每一笔撮合成交发生时。如果一个大单分多笔成交,该函数会被触发多次。
  • 用途:这是确认“钱货两清”的最快方式,适合高频策略或需要即时对冲的策略。
  • 数据结构trade_list 中的元素包含 business_amount (成交量), business_price (成交价), business_time (成交时间) 等。

注意事项

  1. 死循环风险:在回调函数(尤其是 on_order_response)中再次调用 order 系列下单接口时要非常小心。如果逻辑判断不严谨(例如没有检查状态就下单),可能会导致“下单 -> 触发回调 -> 再下单 -> 再触发回调”的无限循环。
  2. 运行环境:这些回调函数主要用于交易模式。在回测模式中,通常依靠 handle_data 的撮合机制,回调函数的触发机制可能与实盘不完全一致或不可用。
  3. 非本策略订单:默认情况下,策略可能只接收本策略产生的订单推送。如果需要接收账户内其他渠道(如手机端手动下单)产生的推送,需要在 initialize 中设置:
    set_parameters(receive_other_response="1")
    

Q&A

Q: 为什么不建议在 handle_data 里用 get_orders() 轮询?
A: handle_data 的运行频率通常是分钟级或日线级,即使是 tick_data 也是3秒一次。而主推回调(Response)是基于柜台推送的,延迟通常在毫秒级,对于需要“立刻”执行的操作,轮询的延迟太高了。

Q: on_order_response 里状态是 '8' (已成) 和 on_trade_response 有什么区别?
A: on_order_response 关注的是订单状态的变更(例如从“已报”变为“已成”),它反映的是订单生命周期的终结。而 on_trade_response 关注的是成交动作本身,包含具体的成交价格和数量。如果一笔订单分5次成交,on_trade_response 会触发5次,而 on_order_response 的“已成”状态通常只在全部成交完成后触发一次(或状态变更时触发)。处理成交逻辑建议用 on_trade_response