🚀 AI 一键生成 ptrade 策略代码

PTrade 实盘交易中遇到委托失败或“废单”怎么办?

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

问题描述

我的订单变成废单了,该怎么处理?

解决方案

在PTrade量化交易平台中,订单变成“废单”(状态码为 '9')通常意味着委托未能通过柜台系统的校验,或者被交易所拒绝。

处理废单的核心思路分为两步:1. 查明原因(通过日志或回调函数)2. 优化策略逻辑(修正下单价格、数量或风控检查)

以下是详细的排查步骤、常见原因及代码处理方案。

一、 为什么会出现废单?常见原因分析

  1. 资金或持仓不足
    • 买入时可用资金不足(需考虑手续费)。
    • 卖出时可用持仓不足(注意T+1规则,今日买入的股票明日才能卖)。
  2. 价格问题
    • 委托价格超过了当天的涨跌停板限制(“价格笼子”)。
    • 在集合竞价阶段使用了不支持的订单类型。
  3. 数量问题
    • 股票买入必须是100股的整数倍(零股卖出除外)。
    • 科创板/创业板有最小200股的限制。
    • 下单数量超过了交易所规定的单笔上限。
  4. 标的状态异常
    • 股票停牌、退市整理期或未上市。
    • 没有开通相应的交易权限(如创业板、科创板、ST股权限)。
  5. 非交易时间
    • 在非交易时间段发送了委托(虽然PTrade通常会缓存,但某些柜台设置可能直接拒单)。

二、 代码处理方案

在策略中,最有效的处理方式是利用 on_order_response(委托主推回调)函数。该函数比轮询查询更快,且返回的字典中包含 error_info 字段,能直接告诉你废单原因。

示例代码:自动监测废单并打印错误信息

def initialize(context):
    # 初始化策略
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 开启委托主推接收(部分券商环境需要显式开启)
    # set_parameters(receive_other_response="1") 

def handle_data(context, data):
    # 示例:故意下一个价格极低或极高的单子测试废单
    # 比如以涨停价+10%买入,通常会导致废单
    current_price = data[g.security]['close']
    # 下单买入100股
    order(g.security, 100, limit_price=current_price * 1.2)

def on_order_response(context, order_list):
    """
    委托回报主推接口
    当订单状态发生变化时触发
    """
    for order_info in order_list:
        # 获取订单状态
        status = order_info.get('status')
        
        # 状态 '9' 代表废单
        if status == '9':
            entrust_no = order_info.get('entrust_no')
            stock_code = order_info.get('stock_code')
            error_msg = order_info.get('error_info') # 获取具体的废单原因
            
            log.error("【出现废单】 股票: %s, 委托编号: %s" % (stock_code, entrust_no))
            log.error("【废单原因】: %s" % error_msg)
            
            # 策略逻辑修正建议:
            # 1. 如果是资金不足 -> 可以在这里减少下单数量重新报单
            # 2. 如果是价格超限 -> 重新获取最新tick价格报单
            # 3. 停止该标的当天的后续交易,防止死循环
            
        # 状态 '8' 代表已成
        elif status == '8':
            log.info("订单已成交: %s" % order_info.get('stock_code'))

三、 如何预防废单(策略优化建议)

在下单前进行预检查,可以大幅减少废单率:

  1. 检查涨跌停价格
    使用 get_snapshotget_price 获取当日的 up_px (涨停价) 和 down_px (跌停价),确保 limit_price 在此区间内。

    snapshot = get_snapshot('600570.SS')
    up_limit = snapshot['600570.SS']['up_px']
    down_limit = snapshot['600570.SS']['down_px']
    # 下单前判断价格是否合规
    
  2. 检查资金是否充足
    order 之前,检查 context.portfolio.cash

  3. 检查标的状态
    使用 get_stock_status 检查股票是否停牌。

    status = get_stock_status(['600570.SS'], 'HALT')
    if status['600570.SS'] == True:
        log.info("股票停牌,停止下单")
    
  4. 使用 order_target 系列函数
    如果是因为计算持仓数量麻烦导致卖出超过持仓,建议使用 order_target(调整至目标仓位)或 order_target_value,API会自动计算需要买卖的数量,避免卖出超过持仓导致的废单。

Q&A 常见问题解答

Q1: 废单会扣除手续费吗?
A: 不会。废单意味着委托未成交,不会产生印花税、佣金或过户费。

Q2: 为什么回测没问题,实盘全是废单?
A: 回测环境通常是理想化的,可能没有严格校验“价格笼子”(如科创板下单价格不能超过基准价的2%)或最小交易单位。实盘中,券商柜台的风控规则更严格。请检查实盘下单价格是否偏离现价过多。

Q3: 收到废单后,我应该立即重发订单吗?
A: 不建议立即无脑重发。如果废单原因是“资金不足”或“涨跌停限制”,立即重发只会导致连续废单,甚至可能触发券商的异常交易监控。正确的做法是解析 error_info,修正价格或数量后再尝试,或者跳过该标的。

Q4: get_orders() 能查到废单吗?
A: 可以。get_orders() 返回的对象中,status 字段为 '9' 的即为废单。但 on_order_response 实时性更强,更适合做盘中即时处理。