问题描述
解决方案
在PTrade中,要获取下单失败的信息和原因,主要通过检查订单的 状态(status) 和 错误信息(error_info) 来实现。
通常有以下两种方式:
-
使用
on_order_response回调函数(推荐,仅实盘/模拟盘有效):
这是最快获取废单信息的方法。当订单状态发生变化(如变为废单)时,系统会自动触发该函数。返回的字典中直接包含error_info字段。 -
轮询
get_orders或get_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. 尝试修正价格重新下单
关键点解析
-
on_order_response(context, order_list):- 参数
order_list是一个包含字典的列表。 - 字典中的
error_info字段是关键,它包含了券商柜台返回的具体错误文本。 - 此函数是事件驱动的,比在
handle_data里轮询更节省资源且反应更快。
- 参数
-
status状态码对照:'2': 已报(正常排队中)'8': 已成(全部成交)'9': 废单(重点关注这个状态)
-
回测与实盘的区别:
- 在回测中,由于没有真实的柜台交互,很多因资金不足或撮合规则导致的失败可能不会触发
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。但务必设置重试次数限制,防止因死循环导致频繁报单被券商风控锁定。