问题描述
解决方案
在PTrade中,要实时跟踪订单的完整状态变化(如下单、部分成交、完全成交),最推荐且最高效的方式是使用 事件驱动的回调函数。
PTrade 提供了两个关键的回调接口:
on_order_response(context, order_list):委托主推。当订单状态发生变化(如变为“已报”、“部成”、“已成”、“废单”等)时,该函数会被立即触发。on_trade_response(context, trade_list):成交主推。当订单发生具体的成交动作(即有一笔撮合成功)时,该函数会被触发。
相比于在 handle_data 中轮询 get_order(),使用这两个回调函数能以更低的延迟获取状态更新。
核心状态码说明
在处理回调数据时,需要关注 status 字段:
"2": 已报 (Submitted)"7": 部成 (Partial Filled) - 此时订单还有剩余数量未成交"8": 已成 (Filled) - 订单全部成交"9": 废单 (Rejected)"6": 已撤 (Canceled)"5": 部撤 (Partial Canceled)
策略代码示例
以下代码展示了如何下单,并通过回调函数实时打印订单从“已报”到“成交”的全过程日志。
def initialize(context):
"""
初始化函数
"""
# 设置股票池
g.security = '600570.SS'
set_universe(g.security)
# 用于记录我们关注的订单ID
g.my_order_id = None
# 标记是否已下单,防止重复下单
g.has_ordered = False
def handle_data(context, data):
"""
盘中运行函数
"""
# 仅为了演示:如果还没下单,且有行情,则下一个限价单
if not g.has_ordered:
# 获取当前价格
current_price = data[g.security]['close']
# 下单买入 1000 股,使用限价单(Limit Order)
# 注意:为了演示部分成交或成交过程,这里以当前价下单
g.my_order_id = order(g.security, 1000, limit_price=current_price)
if g.my_order_id:
log.info("【策略下单】下单成功,订单ID: %s,等待回调更新状态..." % g.my_order_id)
g.has_ordered = True
def on_order_response(context, order_list):
"""
委托主推回调:当订单状态发生变化时触发(如:已报、部成、已成、撤单等)
"""
for order_info in order_list:
# 过滤出我们自己下的那个订单
if order_info['order_id'] == g.my_order_id:
status = order_info['status']
entrust_no = order_info['entrust_no']
# 解析状态
status_desc = "未知状态"
if status == '2':
status_desc = "已报 (等待成交)"
elif status == '7':
status_desc = "部成 (部分成交,剩余待成交)"
elif status == '8':
status_desc = "已成 (全部成交)"
elif status == '9':
status_desc = "废单"
elif status == '6':
status_desc = "已撤"
elif status == '5':
status_desc = "部撤"
log.info("【委托状态更新】订单ID: %s, 委托编号: %s, 当前状态: %s (%s), 已成交数量: %s" % (
g.my_order_id,
entrust_no,
status,
status_desc,
order_info['business_amount']
))
# 如果完全成交,可以在这里做后续逻辑处理
if status == '8':
log.info(">>> 订单 %s 已全部完成,任务结束。" % g.my_order_id)
def on_trade_response(context, trade_list):
"""
成交主推回调:当发生具体成交动作时触发(比on_order_response更细节,关注每一笔撮合)
"""
for trade_info in trade_list:
# 过滤出我们自己下的那个订单
if trade_info['order_id'] == g.my_order_id:
log.info("【成交明细推送】订单ID: %s 发生一笔成交!成交数量: %s, 成交价格: %s, 成交金额: %s" % (
g.my_order_id,
trade_info['business_amount'],
trade_info['business_price'],
trade_info['business_balance']
))
代码解析
- 下单记录:在
handle_data中下单后,我们将返回的order_id保存在全局变量g.my_order_id中,以便后续在回调中筛选。 on_order_response(委托主推):- 这是跟踪宏观状态的最佳位置。
- 当订单刚报到柜台时,状态变为
'2'(已报)。 - 当撮合了一部分但未全部完成时,状态变为
'7'(部成)。 - 当全部撮合完成时,状态变为
'8'(已成)。 - 我们通过判断
status字段来打印对应的日志。
on_trade_response(成交主推):- 这是跟踪微观成交的最佳位置。
- 如果一个 1000 股的订单分成了两笔 500 股成交,这个函数会被调用两次。
- 它提供了具体的成交价格和成交量,适合用于精确计算均价或记录流水。
注意事项
- 回测 vs 实盘:在回测模式下,由于撮合机制是模拟的,通常订单会瞬间从“已报”变为“已成”,可能看不到“部成”的过程(除非成交量受限)。在实盘或仿真交易中,这些状态变化会非常清晰。
- 数据类型:回调函数返回的
order_list和trade_list中的元素是字典 (dict) 类型,而不是对象,因此访问属性时必须使用['key']的方式(如order_info['status']),而不是.status。 - 速度:这两个回调函数的响应速度快于
get_order()查询,适合高频或对时效性要求高的策略。
Q&A
Q: 为什么我在回测里看不到“部成”状态?
A: 回测引擎通常基于分钟线或日线撮合,假设流动性充足。除非您设置了严格的成交量限制(如 set_volume_ratio),否则小额订单通常会一次性完全成交,直接从状态 '2' 跳到 '8'。
Q: on_order_response 和 get_order() 有什么区别?
A: get_order() 是主动查询(轮询),通常在 handle_data 中调用,受限于策略运行频率(如每分钟一次)。on_order_response 是被动接收(推送),一旦柜台状态变化立即触发,延迟更低,且能捕获到中间状态。
Q: 如果我不使用回调,怎么查询订单状态?
A: 您可以在 handle_data 中使用 curr_order = get_order(g.my_order_id),然后检查 curr_order.status。但这只能获取调用那一刻的状态,可能会漏掉中间快速变化的过程。