🚀 AI 一键生成 ptrade 策略代码

PTrade 策略如何实时捕获“废单”等交易错误并自动处理?

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

问题描述

我的下单指令有时候会失败,怎样才能在代码里知道下单失败了,并且看到失败的原因?

解决方案

在PTrade中,要获取下单失败的信息和原因,主要通过检查订单的 状态(status)错误信息(error_info) 来实现。

通常有以下两种方式:

  1. 使用 on_order_response 回调函数(推荐,仅实盘/模拟盘有效):
    这是最快获取废单信息的方法。当订单状态发生变化(如变为废单)时,系统会自动触发该函数。返回的字典中直接包含 error_info 字段。

  2. 轮询 get_ordersget_order(回测与实盘均可用):
    handle_data 或其他周期函数中,主动获取订单对象,检查其 status 是否为 '9'(代表废单/Rejected)。

核心概念说明

  • Status '9' (废单): 这是PTrade中表示订单被柜台或交易所拒绝的状态码。
  • error_info: 具体的失败原因(如“可用资金不足”、“非交易时间”、“价格超出涨跌停限制”等)。

策略代码示例

以下代码展示了如何通过上述两种方式捕获下单失败的情况。

def initialize(context):
    # 初始化策略
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 设置一个标志,用于演示下单
    g.order_placed = False

    # 开启接收非本策略生成的订单主推(可选,视需求而定)
    set_parameters(receive_other_response="1")

def handle_data(context, data):
    # 示例:在第一天尝试下一个明显会导致废单的委托(例如跌停价卖出没有持仓的股票,或者资金不足买入)
    if not g.order_placed:
        # 这里故意下一个可能失败的单子,或者正常下单
        # 假设我们没有持仓,尝试卖出,这通常会导致废单
        log.info("尝试下单卖出 %s ..." % g.security)
        order_id = order(g.security, -100) 
        
        if order_id:
            log.info("下单接口调用成功,订单ID: %s,等待柜台回报..." % order_id)
        else:
            log.info("下单接口直接返回None,可能是参数错误或风控拦截")
            
        g.order_placed = True

    # --- 方法二:轮询方式 (适用于回测和实盘) ---
    # 获取当日所有订单
    all_orders = get_orders()
    for o in all_orders:
        # 检查状态是否为 '9' (废单)
        if o.status == '9':
            # 注意:在回测中,Order对象可能不包含详细的error_info文本,但在实盘中通常会有
            # 这里主要演示逻辑
            log.info("【轮询检测】发现废单!订单号: %s" % o.entrust_no)

def on_order_response(context, order_list):
    """
    方法一:委托主推回调 (仅交易模块/实盘可用)
    当订单状态变化时,此函数会被触发。
    这是捕获失败原因最准确、最及时的地方。
    """
    for order_dict in order_list:
        # 打印整条推送信息以便调试
        # log.info("收到委托主推: %s" % order_dict)
        
        # 获取订单状态
        status = order_dict.get('status')
        
        # 状态 '9' 代表废单
        if status == '9':
            stock_code = order_dict.get('stock_code')
            entrust_no = order_dict.get('entrust_no')
            error_info = order_dict.get('error_info') # 获取失败原因
            
            log.error("【主推报警】下单失败!")
            log.error("标的: %s" % stock_code)
            log.error("委托编号: %s" % entrust_no)
            log.error("失败原因: %s" % error_info)
            
            # 在这里可以添加策略逻辑,比如:
            # 1. 发送邮件通知 set_email_info(...)
            # 2. 停止策略交易
            # 3. 尝试修正价格重新下单

关键点解析

  1. on_order_response(context, order_list):

    • 参数 order_list 是一个包含字典的列表。
    • 字典中的 error_info 字段是关键,它包含了券商柜台返回的具体错误文本。
    • 此函数是事件驱动的,比在 handle_data 里轮询更节省资源且反应更快。
  2. status 状态码对照:

    • '2': 已报(正常排队中)
    • '8': 已成(全部成交)
    • '9': 废单(重点关注这个状态)
  3. 回测与实盘的区别:

    • 回测中,由于没有真实的柜台交互,很多因资金不足或撮合规则导致的失败可能不会触发 on_order_response,或者 error_info 为空。回测引擎通常会直接在日志窗口输出 WARNING 级别的日志提示(例如 "Order canceled due to insufficient funds")。
    • 实盘/模拟盘中,on_order_response 是捕获废单的最佳方式。

常见失败原因 (error_info 可能出现的内容)

  • 资金不足: "可用资金不足"
  • 股份不足: "可用股份不足" (卖出时)
  • 价格限制: "委托价格超出涨跌幅限制"
  • 合规限制: "客户风险等级不匹配" 或 "该证券被限制买入"
  • 交易时间: "非交易时间"
  • 最小单位: "委托数量不足最小交易单位"

Q&A

Q1: 为什么我在回测里使用了 on_order_response 却没有打印日志?
A1: on_order_response 主要用于交易模块(实盘或仿真)。在纯回测模式下,PTrade的撮合引擎行为与真实柜台不同,它可能直接在后台处理了无效订单并在日志中打印系统警告,而不一定触发这个回调函数。回测中建议关注左下角的日志输出。

Q2: 如果 order() 函数直接返回 None 是什么意思?
A2: 如果 order() 函数直接返回 None 而不是订单ID,说明委托在策略端就被拦截了,根本没有发送到柜台。这通常是因为参数错误(如代码格式错误、数量为0等)。这种情况不会触发 on_order_response,需要检查代码逻辑。

Q3: 如何在收到废单后自动重单?
A3: 可以在 on_order_response 中判断 status == '9' 后,根据 error_info 的内容编写逻辑。例如,如果原因是“价格偏离”,可以获取最新 get_snapshot 价格后再次调用 order。但务必设置重试次数限制,防止因死循环导致频繁报单被券商风控锁定。