问题描述
解决方案
在PTrade量化交易平台中,“部成待撤”(Partially Filled, Pending Cancel)是一种中间状态,表示订单已经部分成交,且策略或用户已经发起了撤单请求,但柜台或交易所尚未最终确认撤单结果。
以下是关于如何判断和处理该状态的详细说明及代码示例。
1. 状态定义与判断方法
根据PTrade API文档的数据字典,“部成待撤”对应的订单状态码为 '4'。
- 含义:订单已成交一部分数量,剩余部分正在撤单过程中。
- 判断方式:通过
get_orders()获取订单对象,检查其status属性是否等于'4'。
2. 处理逻辑建议
由于“部成待撤”是一个过渡状态,通常不需要(也无法)对其再次发起撤单操作。处理策略通常如下:
- 等待状态流转:该状态通常很快会变为“部撤”(状态码
'5',即撤单成功)或“已成”(状态码'8',即撤单失败,剩余部分全部成交)。 - 风控锁死:在状态未流转为最终状态(如部撤、已成)之前,不要对该标的进行新的下单操作,以免造成资金占用计算错误或重复下单。
- 超时处理(仅限实盘):如果在实盘中该状态持续时间过长(如超过1分钟),可能涉及柜台异常,建议通过日志报警人工介入。
3. 代码实现
以下策略代码展示了如何在 handle_data 中轮询订单,识别“部成待撤”的订单,并进行相应的逻辑处理(如打印日志、暂停对该标的的后续操作)。
def initialize(context):
# 初始化股票池
g.security = '600570.SS'
set_universe(g.security)
def handle_data(context, data):
# 获取当日所有订单
orders = get_orders()
# 标记是否存在“部成待撤”的订单
has_pending_cancel = False
for _order in orders:
# 状态码 '4' 代表 部成待撤
if _order.status == '4':
log.info("发现部成待撤订单,订单号: %s, 标的: %s" % (_order.order_id, _order.symbol))
has_pending_cancel = True
# 在此处可以添加逻辑,例如记录到全局变量,防止下一轮继续操作该标的
# g.locked_securities.append(_order.symbol)
# 状态码 '3' 代表 已报待撤 (尚未成交但正在撤单)
elif _order.status == '3':
log.info("发现已报待撤订单,订单号: %s" % _order.order_id)
# 正常的交易逻辑
if not has_pending_cancel:
# 只有当没有正在撤单的中间状态时,才进行新的交易判断
# 示例:简单的买入逻辑
if get_position(g.security).amount == 0:
order(g.security, 100)
else:
log.info("当前有订单处于撤单中间状态,暂停新开仓操作。")
4. 关键点总结
- 状态码对照:
'3': 已报待撤(完全未成交,正在撤)'4': 部成待撤(部分成交,剩余正在撤)'5': 部撤(最终状态,部分成交,剩余已撤销)
- API兼容性:代码使用了
%s格式化字符串,完全兼容 Python 3.5 及以上版本。 - 实盘注意:在回测环境中,由于撮合是瞬间完成的,很难捕捉到
'4'这种瞬时状态;该逻辑主要用于实盘交易中的状态防抖和风控。
Q&A
Q1: 为什么我的策略里捕捉不到状态 '4'?
A1: 在回测模式下,PTrade的撮合引擎处理速度极快,订单状态通常直接从“已报”跳变为“已成”或“已撤”,很难捕捉到“待撤”这种毫秒级的中间状态。该逻辑主要在实盘交易中有效。
Q2: 如果订单一直卡在“部成待撤”怎么办?
A2: 如果在实盘中该状态持续超过几秒钟甚至更久,通常意味着柜台或交易所回报延迟。此时再次调用 cancel_order 通常无效。建议在策略中设置超时告警,并联系券商或通过终端手动检查。
Q3: cancel_order 函数能撤销状态为 '4' 的订单吗?
A3: 不需要也不建议。状态为 '4' 说明系统已经接收到了撤单指令并正在处理中。重复调用 cancel_order 可能会导致API报错或被柜台视为频繁报撤单。