问题描述
解决方案
在PTrade中,若要在下单委托成功(报单成功)或成交后立刻执行下一步操作,最推荐且响应速度最快的方法是使用交易主推回调函数。
相比于在 handle_data 或 tick_data 中轮询查询订单状态,回调函数是事件驱动的,即当柜台返回委托状态变化或成交回报时,系统会自动触发这些函数。
核心解决方案
- 委托成功(已报):使用
on_order_response(context, order_list)。 - 成交成功:使用
on_trade_response(context, trade_list)。
注意:这两个函数仅在交易模式(实盘或仿真交易)下可用,回测模式下通常不支持或行为不同。
代码实现示例
以下策略展示了如何定义这两个函数,并在收到回报时立即打印日志或执行逻辑。
def initialize(context):
# 初始化股票池
g.security = '600570.SS'
set_universe(g.security)
# 设置一个标志位,防止handle_data重复下单,仅作演示用
g.order_placed = False
def handle_data(context, data):
# 示例:在盘中下一个买单
if not g.order_placed:
log.info("开始下单...")
# 下单买入100股
order(g.security, 100)
g.order_placed = True
# ---------------------------------------------------------
# 1. 委托主推回调:当订单状态发生变化时(如:已报、废单、已撤等)触发
# ---------------------------------------------------------
def on_order_response(context, order_list):
"""
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')
# 状态 '2' 代表 "已报" (委托成功到达柜台)
if status == '2':
log.info("【委托成功回调】股票: %s, 委托编号: %s, 状态: 已报" % (stock_code, entrust_no))
# --- 在此处编写委托成功后立刻要执行的逻辑 ---
# 例如:记录日志、更新自定义变量等
# 注意:尽量避免在此处直接再次下单,除非有严格的逻辑控制,否则容易造成死循环
# 状态 '9' 代表 "废单"
elif status == '9':
log.warning("【废单回调】股票: %s, 原因: %s" % (stock_code, order_info.get('error_info')))
# ---------------------------------------------------------
# 2. 成交主推回调:当订单发生生成交时触发
# ---------------------------------------------------------
def on_trade_response(context, trade_list):
"""
trade_list 是一个列表,包含最新的成交记录字典
"""
for trade_info in trade_list:
stock_code = trade_info.get('stock_code')
amount = trade_info.get('business_amount') # 成交数量
price = trade_info.get('business_price') # 成交价格
direction = trade_info.get('entrust_bs') # 1-买,2-卖
direction_str = "买入" if direction == '1' else "卖出"
log.info("【成交回调】股票: %s, 方向: %s, 数量: %s, 价格: %s" % (stock_code, direction_str, amount, price))
# --- 在此处编写成交后立刻要执行的逻辑 ---
# 例如:
# 1. 立即对冲(如期现套利)
# 2. 止盈止损单挂单
# 3. 策略仓位更新
# 示例:买入成交后,立即挂一个高价卖单(网格交易常用逻辑)
if direction == '1': # 如果是买入成交
target_price = price * 1.02 # 目标利润2%
# 注意:在回调中下单务必谨慎,防止逻辑错误导致无限下单
# order(stock_code, -amount, limit_price=target_price)
log.info("计划卖出价格: %s" % target_price)
关键点解析
1. on_order_response (委托主推)
- 触发时机:订单状态发生任何改变时(变为已报、部成、已成、已撤、废单等)。
- 判断委托成功:检查
status字段是否为'2'。 - 数据结构:
order_list中的元素包含entrust_no(委托编号),status(状态),order_id(PTrade内部ID) 等。
2. on_trade_response (成交主推)
- 触发时机:每一笔撮合成交发生时。如果一个大单分多笔成交,该函数会被触发多次。
- 用途:这是确认“钱货两清”的最快方式,适合高频策略或需要即时对冲的策略。
- 数据结构:
trade_list中的元素包含business_amount(成交量),business_price(成交价),business_time(成交时间) 等。
注意事项
- 死循环风险:在回调函数(尤其是
on_order_response)中再次调用order系列下单接口时要非常小心。如果逻辑判断不严谨(例如没有检查状态就下单),可能会导致“下单 -> 触发回调 -> 再下单 -> 再触发回调”的无限循环。 - 运行环境:这些回调函数主要用于交易模式。在回测模式中,通常依靠
handle_data的撮合机制,回调函数的触发机制可能与实盘不完全一致或不可用。 - 非本策略订单:默认情况下,策略可能只接收本策略产生的订单推送。如果需要接收账户内其他渠道(如手机端手动下单)产生的推送,需要在
initialize中设置:set_parameters(receive_other_response="1")
Q&A
Q: 为什么不建议在 handle_data 里用 get_orders() 轮询?
A: handle_data 的运行频率通常是分钟级或日线级,即使是 tick_data 也是3秒一次。而主推回调(Response)是基于柜台推送的,延迟通常在毫秒级,对于需要“立刻”执行的操作,轮询的延迟太高了。
Q: on_order_response 里状态是 '8' (已成) 和 on_trade_response 有什么区别?
A: on_order_response 关注的是订单状态的变更(例如从“已报”变为“已成”),它反映的是订单生命周期的终结。而 on_trade_response 关注的是成交动作本身,包含具体的成交价格和数量。如果一笔订单分5次成交,on_trade_response 会触发5次,而 on_order_response 的“已成”状态通常只在全部成交完成后触发一次(或状态变更时触发)。处理成交逻辑建议用 on_trade_response。