问题描述
解决方案
在PTrade中,要根据订单的实时状态变化(如“已成”、“废单”、“已撤”等)来执行下一步操作,最推荐的方式是使用 委托主推回调函数 on_order_response。
相比于在 handle_data 中轮询查询订单状态,on_order_response 是由交易柜台或系统在订单状态发生变更时主动触发的,具有响应速度快、实时性强的特点。
核心机制
on_order_response(context, order_list): 当委托单状态发生变化时(如变为已报、部成、已成、废单等),系统会自动调用此函数。order_list: 这是一个列表,包含发生变化的订单信息字典。status字段: 字典中的status字段代表当前订单的状态(例如:'8'代表全部成交,'9'代表废单)。
策略逻辑示例
下面的策略展示了如何实现以下逻辑:
- 在盘中下单买入股票。
- 通过
on_order_response监听订单状态。 - 如果订单全部成交(Status='8'),则立即执行下一步操作(例如:打印日志或下止盈单)。
- 如果订单废单(Status='9'),则记录错误信息。
PTrade 策略代码
def initialize(context):
# 初始化策略
g.security = '600570.SS' # 恒生电子
set_universe(g.security)
# 设置一个标志位,防止handle_data重复下单
g.order_sent = False
def before_trading_start(context, data):
# 每日盘前重置标志位
g.order_sent = False
def handle_data(context, data):
# 示例:在盘中简单下单
if not g.order_sent:
# 下单买入100股,限价40元(仅为示例,请根据行情调整)
# 注意:这里我们记录了返回的 order_id,虽然在回调中主要通过 entrust_no 或 order_id 匹配
g.current_order_id = order(g.security, 100, limit_price=40)
if g.current_order_id:
log.info("已发送买入委托,订单ID: %s" % g.current_order_id)
g.order_sent = True
def on_order_response(context, order_list):
"""
委托主推回调函数
当订单状态发生变化时,本函数会被触发
"""
for order_info in order_list:
# 获取订单的关键信息
status = order_info.get('status')
entrust_no = order_info.get('entrust_no')
stock_code = order_info.get('stock_code')
order_id = order_info.get('order_id')
log.info("收到订单更新 - 代码: %s, 委托编号: %s, 状态: %s" % (stock_code, entrust_no, status))
# 根据状态执行下一步操作
# 状态 '8' 代表 "已成" (全部成交)
if status == '8':
log.info("【策略触发】订单已全部成交!准备执行下一步操作...")
# --- 在这里写成交后的逻辑 ---
# 例如:记录成交,或者挂出止损单
# 注意:如果在回调中再次下单,务必做好逻辑控制,防止死循环
do_next_step(context, stock_code)
# 状态 '9' 代表 "废单"
elif status == '9':
error_info = order_info.get('error_info', '未知错误')
log.error("【策略警告】订单废单!原因: %s" % error_info)
# --- 在这里写废单后的逻辑 ---
# 例如:重置标志位允许重新下单
g.order_sent = False
# 状态 '6' 代表 "已撤"
elif status == '6':
log.info("【策略提示】订单已撤销。")
g.order_sent = False
def do_next_step(context, stock_code):
"""
自定义的下一步操作函数
"""
log.info("正在为 %s 执行后续逻辑(如:更新持仓记录、发送通知等)" % stock_code)
# 示例:获取当前持仓
pos = get_position(stock_code)
log.info("当前持仓数量: %s" % pos.amount)
关键状态码说明 (Data Dictionary)
在 on_order_response 中,status 字段是判断的核心依据。根据 PTrade 文档,常用状态码如下:
| 状态码 | 含义 | 说明 |
|---|---|---|
| 2 | 已报 | 订单已报送至交易所,等待撮合 |
| 4 | 部成待撤 | 部分成交,剩余部分已申请撤单 |
| 5 | 部撤 | 部分成交,剩余部分已撤单 |
| 6 | 已撤 | 订单已全部撤销 |
| 7 | 部成 | 部分成交,剩余部分仍在挂单 |
| 8 | 已成 | 全部成交 (最常用的判断状态) |
| 9 | 废单 | 订单被柜台或交易所拒绝 (需检查价格、资金等) |
注意事项
-
死循环风险:
如果在on_order_response中直接调用order系列函数进行下单,而新下的单又触发了状态变化(如废单),可能会导致无限递归调用。务必在回调中加入逻辑判断(如检查持仓、设置特定的全局Flag)来避免死循环。 -
适用环境:
on_order_response主要用于**交易(实盘/模拟盘)**环境。在回测环境中,虽然也支持该回调,但回测引擎的撮合机制是基于K线或Tick模拟的,状态变化可能不如实盘那样丰富(例如回测中很少出现“已报”这种中间状态,通常直接跳到“已成”)。 -
数据类型:
order_list中的数据是字典(dict),且部分字段(如amount,price)可能是浮点数或整数,而status是字符串。比较状态时请使用字符串'8'而不是数字8。 -
成交主推 (
on_trade_response):
如果你更关注成交细节(如具体的成交价格、成交时间、分笔成交情况),可以使用on_trade_response。但如果你只关心订单的最终生命周期(是否这就完了、是否废了),on_order_response更为宏观和直接。
Q&A
Q: on_order_response 和 get_orders() 有什么区别?
A: on_order_response 是事件驱动的,只有当状态变了才会触发,实时性极高,适合高频或对时效要求高的策略。get_orders() 是主动查询,通常在 handle_data 中轮询使用,适合低频策略或做定期状态检查。
Q: 为什么我在回测中收不到 '2' (已报) 的状态?
A: 回测引擎通常为了效率会简化撮合过程。如果你的订单满足撮合条件,回测引擎可能直接将其状态置为 '8' (已成),跳过了中间的申报过程。
Q: 如果我想在部分成交(部成)时也执行操作怎么办?
A: 可以在 on_order_response 中监听 status 为 '7' (部成) 的情况。但要注意,一个订单可能会多次触发“部成”状态(每次有新成交时),或者同时触发 on_trade_response。建议结合 filled_amount(已成交数量)来判断进度。