问题描述
解决方案
在PTrade中,要获取当前所有未成交的挂单并将其全部撤销,主要涉及两个API函数:
get_open_orders(): 获取当前所有未完成(未成交、部成待撤等状态)的订单。cancel_order(order_param): 对指定的订单进行撤单操作。
实现逻辑
- 调用
get_open_orders()获取一个包含所有未完成订单对象的列表。 - 遍历这个列表。
- 对列表中的每一个订单对象调用
cancel_order()。
代码示例
以下是一个完整的策略示例代码。为了演示效果,我在 initialize 中设置了股票池,并定义了一个 cancel_all_open_orders 函数,你可以在 handle_data 或其他逻辑中调用它。
def initialize(context):
# 初始化设置
g.security = '600570.SS'
set_universe(g.security)
# 为了演示,这里设置一个定时任务,每天14:50执行撤单操作
# 实际使用时,你可以根据自己的策略逻辑在任何地方调用撤单函数
run_daily(context, cancel_all_open_orders, time='14:50')
def handle_data(context, data):
# 示例:每个交易时刻尝试下单(为了产生挂单供测试撤单使用)
# 实际策略中请根据你的逻辑下单
# 下一个价格很低的买单,确保它不会立即成交,从而产生挂单
current_price = data[g.security]['close']
if current_price > 0:
order(g.security, 100, limit_price=current_price * 0.9)
def cancel_all_open_orders(context):
"""
自定义函数:获取所有未完成订单并撤销
"""
# 1. 获取当前所有未完成的订单
# 不传参数默认获取所有标的的未完成订单
open_orders = get_open_orders()
if not open_orders:
log.info("当前没有未完成的挂单。")
return
log.info("检测到 %s 个未完成订单,开始执行撤单..." % len(open_orders))
# 2. 遍历订单列表并逐个撤销
for _order in open_orders:
# 记录日志
log.info("正在撤销订单 -> 订单号: %s, 标的: %s, 数量: %s" % (_order.order_id, _order.symbol, _order.amount))
# 3. 执行撤单
# cancel_order 既可以接收 order_id 字符串,也可以接收 Order 对象
cancel_order(_order)
代码详解
-
get_open_orders():- 该函数返回一个列表,列表中的每个元素都是一个
Order对象。 - 如果不传入
security参数,它会返回账户下所有标的的未完成订单。 - 如果只想撤销特定股票的订单,可以传入参数,例如
get_open_orders(security='600570.SS')。
- 该函数返回一个列表,列表中的每个元素都是一个
-
循环与撤单:
- 使用
for循环遍历open_orders列表。 cancel_order(_order):直接将订单对象传入即可完成撤单。也可以传入_order.order_id。
- 使用
-
注意事项:
- Python 版本兼容性: PTrade 环境通常基于 Python 3,但为了兼容性(特别是旧版 Python 3.5),代码中使用了
%格式化字符串(如"%s" % var),而不是 f-string。 - 交易与回测: 该逻辑在回测和实盘交易中均适用。在实盘交易中,撤单指令发送到柜台可能有一点延迟,但在策略层面调用后即视为已发出撤单请求。
- 撤单失败: 如果订单在撤单指令发出前刚好成交,撤单可能会失败(或提示无法撤单),这是正常现象。
- Python 版本兼容性: PTrade 环境通常基于 Python 3,但为了兼容性(特别是旧版 Python 3.5),代码中使用了
Q&A
Q: get_open_orders 和 get_orders 有什么区别?
A: get_open_orders 只返回未完成(如未报、待报、已报、部成)的订单,即“挂单”。而 get_orders 返回当日所有状态的订单,包括已成交、已撤销和废单。撤单逻辑中应使用 get_open_orders。
Q: 如果我只想撤销买单,保留卖单怎么办?
A: 你可以在遍历 open_orders 时判断订单的方向。Order 对象有一个 amount 属性,买入是正数,卖出是负数。
for _order in get_open_orders():
if _order.amount > 0: # 仅撤销买单
cancel_order(_order)
Q: 撤单操作是同步的吗?
A: cancel_order 函数本身是异步发出的指令。调用后,订单状态不会立即变为“已撤”,而是先变为“已报待撤”。你需要等待交易所确认后,订单状态才会更新为“已撤”。