问题描述
解决方案
在 JoinQuant 聚宽平台中,查询未成交订单并将其取消的操作主要涉及两个 API 函数:get_open_orders() 和 cancel_order()。
以下是具体的实现步骤和代码示例。
核心逻辑说明
- 获取未完成订单:使用
get_open_orders()函数。该函数返回一个字典(dict),其中 Key 是订单 ID,Value 是订单对象(Order Object)。 - 遍历并撤单:遍历字典中的所有订单对象,并将它们逐一传入
cancel_order()函数进行撤销。
代码实现
你可以将这段逻辑放在 handle_data 中,或者更常见的是放在 after_trading_end(收盘后)或自定义的定时运行函数中,以确保清理当天的挂单。
以下是一个完整的策略代码示例,展示了如何在每天收盘后自动撤销所有未完成订单:
# -*- coding: utf-8 -*-
def initialize(context):
# 设置基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 每天收盘后执行撤单逻辑
# 注意:回测中未完成订单会在当日结算时自动撤销,但在模拟交易或实盘中,
# 显式撤单是一个好的风控习惯,或者用于盘中策略逻辑控制。
run_daily(cancel_all_open_orders, '14:55')
def handle_data(context, data):
# 示例:每个单位时间随机下一个限价单(为了演示撤单)
# 实际策略中请替换为你的交易逻辑
current_price = data['000001.XSHE'].close
# 下一个很难成交的限价单(买入价为当前价的90%)
order('000001.XSHE', 100, style=LimitOrderStyle(current_price * 0.9))
def cancel_all_open_orders(context):
"""
自定义函数:查询并撤销所有未完成订单
"""
# 1. 获取当前所有未完成的订单
# 返回的是一个dict,key为order_id,value为Order对象
open_orders = get_open_orders()
if len(open_orders) > 0:
log.info("检测到 %d 个未完成订单,开始撤单..." % len(open_orders))
# 2. 遍历字典的 values (即 Order 对象)
for _order in open_orders.values():
# 3. 执行撤单
cancel_order(_order)
log.info("已撤销订单, 标的: %s, 订单ID: %s" % (_order.security, _order.order_id))
else:
log.info("当前没有未完成的订单。")
def after_trading_end(context):
log.info('盘后处理结束')
API 详解
1. get_open_orders()
- 功能:获得当天的所有未完成的订单。
- 参数:无。
- 返回值:一个字典(dict),Key 是
order_id,Value 是Order对象。
2. cancel_order(order)
- 功能:取消订单。
- 参数:
order,可以是Order对象,也可以是order_id(字符串)。 - 返回值:如果取消成功,返回对应的
Order对象;如果订单不存在或已完成,返回None。
常见问题 Q&A
Q: 为什么要显式调用撤单?回测不是会自动撤销吗?
A: 在回测环境中,每天收盘后系统确实会自动撤销未成交的订单。但在模拟交易或实盘中,或者在分钟级策略中(例如需要在当前分钟结束前撤销上一分钟的挂单),显式调用 cancel_order 是必须的,这有助于防止资金被无效挂单占用,或防止过期的信号在错误的时间成交。
Q: get_open_orders() 能获取到已经成交的订单吗?
A: 不能。它只返回状态为 open(已委托未成交)或 new(新创建未委托)的订单。如果订单已经全部成交(held)、已撤销(canceled)或被拒绝(rejected),不会包含在返回结果中。
Q: 如果我想撤销特定股票的订单怎么办?
A: 你可以在遍历时判断订单对象的 security 属性。例如:
orders = get_open_orders()
for _order in orders.values():
if _order.security == '000001.XSHE':
cancel_order(_order)