问题描述
解决方案
在PTrade量化交易平台中,订单变成“废单”(状态码为 '9')通常意味着委托未能通过柜台系统的校验,或者被交易所拒绝。
处理废单的核心思路分为两步:1. 查明原因(通过日志或回调函数),2. 优化策略逻辑(修正下单价格、数量或风控检查)。
以下是详细的排查步骤、常见原因及代码处理方案。
一、 为什么会出现废单?常见原因分析
- 资金或持仓不足:
- 买入时可用资金不足(需考虑手续费)。
- 卖出时可用持仓不足(注意T+1规则,今日买入的股票明日才能卖)。
- 价格问题:
- 委托价格超过了当天的涨跌停板限制(“价格笼子”)。
- 在集合竞价阶段使用了不支持的订单类型。
- 数量问题:
- 股票买入必须是100股的整数倍(零股卖出除外)。
- 科创板/创业板有最小200股的限制。
- 下单数量超过了交易所规定的单笔上限。
- 标的状态异常:
- 股票停牌、退市整理期或未上市。
- 没有开通相应的交易权限(如创业板、科创板、ST股权限)。
- 非交易时间:
- 在非交易时间段发送了委托(虽然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'))
三、 如何预防废单(策略优化建议)
在下单前进行预检查,可以大幅减少废单率:
-
检查涨跌停价格:
使用get_snapshot或get_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'] # 下单前判断价格是否合规 -
检查资金是否充足:
在order之前,检查context.portfolio.cash。 -
检查标的状态:
使用get_stock_status检查股票是否停牌。status = get_stock_status(['600570.SS'], 'HALT') if status['600570.SS'] == True: log.info("股票停牌,停止下单") -
使用
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 实时性更强,更适合做盘中即时处理。