问题描述
解决方案
在 JoinQuant 聚宽平台中,查询未成交挂单并进行撤销的操作主要依赖于两个 API 函数:get_open_orders() 和 cancel_order()。
以下是具体的实现代码和详细说明。
代码实现
你可以将此逻辑封装在一个函数中,或者直接放在 handle_data、after_trading_end 等函数中执行。
# -*- coding: utf-8 -*-
def cancel_open_orders(context):
"""
查询并撤销所有未完成的挂单
"""
# 1. 获取当前所有未完成的订单
# 返回一个 dict, key 是 order_id, value 是 Order 对象
orders = get_open_orders()
# 判断是否有未完成订单
if len(orders) > 0:
log.info("发现未完成订单,准备撤单...")
# 2. 遍历所有未完成的订单对象
for _order in orders.values():
# 3. 执行撤单操作
cancel_order(_order)
log.info("已撤销订单,订单ID: %s, 标的: %s" % (_order.order_id, _order.security))
else:
log.info("当前没有未完成的挂单。")
# 示例:在每天收盘后调用(需要在 initialize 中注册)
def after_trading_end(context):
cancel_open_orders(context)
API 详解
1. get_open_orders()
- 功能:获取当前账户所有未完成的订单(即挂单)。
- 返回值:返回一个字典(dict)。
- Key:订单 ID (
order_id)。 - Value:订单对象 (
OrderObject),包含订单的详细信息(如标的、数量、价格、状态等)。
- Key:订单 ID (
- 注意:该函数不需要传入参数。
2. cancel_order(order)
- 功能:取消指定的订单。
- 参数:
order:可以是Order对象(推荐),也可以是订单 ID 字符串。
- 返回值:如果撤单成功,返回对应的
Order对象;如果订单不存在或已成交/已撤销,返回None。
使用场景建议
-
收盘后清理:
如上面的代码示例,通常在after_trading_end中调用,确保当天的挂单不会遗留到下一个交易日(虽然交易所通常会自动撤销当日未成交的普通委托,但在策略逻辑中显式撤单有助于保持状态清晰)。 -
下单前清理:
在使用order_target(目标股数下单)系列函数前,如果希望重新调整仓位且不受旧挂单影响,可以先执行全额撤单。 -
策略逻辑触发:
例如在追涨杀跌策略中,如果挂单在 N 分钟内未成交,认为行情已经偏离预期,可以在handle_data中判断订单生成时间并进行撤单。
Q&A
Q: get_open_orders() 能获取到已经成交的订单吗?
A: 不能。它只返回状态为 open(已委托未成交)或 new(新创建未委托)的订单。如果订单已经全部成交(held)、已撤销(canceled)或被拒绝(rejected),不会包含在返回结果中。
Q: 如果我只想撤销某一只特定股票的挂单怎么办?
A: get_open_orders() 返回的是字典,你可以遍历它并判断 order.security 属性。例如:
orders = get_open_orders()
for _order in orders.values():
if _order.security == '000001.XSHE': # 仅撤销平安银行的单子
cancel_order(_order)
Q: 模拟交易和回测中,撤单的规则一样吗?
A: 基本一致。但在回测中,撤单是立即生效的;在模拟交易(实盘模拟)中,撤单请求发送给交易所后,需要等待交易所确认,存在极短的延迟。