3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 PTrade 量化交易平台中,on_order_response(委托主推)是一个非常强大的事件驱动接口。它能在委托单状态发生变化时实时回调,比传统的轮询方式(如 get_order)速度更快,非常适合高频交易或对时效性要求极高的策略。
然而,如果在 on_order_response 中调用委托接口(如 order、order_target 等)时处理不当,极易引发无限递归循环(死循环),导致策略瞬间报出大量废单或异常交易,甚至导致账户被柜台风控锁定。
on_order_response 的触发机制是:只要有委托单状态发生变化,引擎就会调用该函数。
如果你在 on_order_response 内部直接或间接地调用了 order() 接口:
on_order_response。order(g.security, 100)。on_order_response。order(g.security, 100)……如此往复,便形成了**“下单 -> 状态改变 -> 触发回调 -> 再次下单”**的无限递归死循环。
要打破这个死循环,必须在回调函数中引入状态控制或条件过滤。以下是三种最常用的避坑方法:
通过全局变量 g 记录策略的执行状态,确保同一逻辑只执行一次。在下单前检查标识,下单后立即修改标识。
不要对所有的主推消息都进行响应。例如,只在特定订单达到“已成('8')”或“已撤('6')”时才触发下一步逻辑,并且要避开“撤单(entrust_type='2')”本身产生的主推。
建立一个已处理订单 ID 的集合(Set),每次收到主推时,先判断该 order_id 是否已经处理过,防止重复响应。
以下是一个安全的 PTrade 策略模板,展示了如何通过全局标识与状态过滤双重保险,安全地在 on_order_response 中进行追单操作:
def initialize(context):
g.security = '600570.SS' # 恒生电子
set_universe(g.security)
# 避坑关键:初始化控制标识
g.next_order_triggered = False
g.first_order_id = None
def handle_data(context, data):
# 盘中触发第一笔初始委托
if g.first_order_id is None:
g.first_order_id = order(g.security, 100)
log.info(f"已发送初始委托,订单ID: {g.first_order_id}")
def on_order_response(context, order_list):
"""
委托主推回调函数
"""
for order_info in order_list:
# 打印主推信息,便于调试
log.info(f"收到委托主推: {order_info}")
# 避坑规则 1:只响应我们关心的初始订单
if order_info['order_id'] != g.first_order_id:
continue
# 避坑规则 2:使用全局标识,确保后续逻辑只执行一次
if g.next_order_triggered:
log.info("后续委托已触发过,跳过处理,防止无限递归。")
continue
# 避坑规则 3:严格限制触发状态(例如:只有在初始订单“已成”时才追单)
# status '8' 代表已成
if order_info['status'] == '8':
log.info("初始订单已完全成交,开始触发第二步委托...")
# 立即修改标识(必须在调用order之前修改,防止多线程或快速回调并发导致重复进入)
g.next_order_triggered = True
# 执行新的委托
new_id = order(g.security, 200)
log.info(f"第二步委托已发送,新订单ID: {new_id}")
on_order_response 中满足条件准备下单时,务必先修改全局状态变量,再调用 order 接口。receive_other_response="1"(接收非本交易产生的主推),主推中的 order_id 可能会是空字符串 "",必须做好过滤,避免对外部手动交易产生误判。