4. 紧急撤单基础

在交易系统的 Kill Switch 体系中,紧急撤单不是“点一下按钮”这么简单。它是一条从策略层、风控层、柜台接口到交易所撮合引擎的完整链路。理解撤单的类型、优先级、延迟构成以及失败原因,是设计可靠 Kill Switch 的前提。本节将拆解紧急撤单的底层机制,帮助你在真实故障场景中做出正确判断。

一、撤单类型:按对象与范围划分

紧急撤单并非只有一种形态。根据撤单对象和触发范围,通常分为以下四类:

  • 单笔撤单(Order Cancel):针对某一笔未成交订单发起撤销,是最细粒度的操作,常用于策略主动纠错。
  • 批量撤单(Batch Cancel):按合约、账户或策略分组,一次性撤销一组订单,适合策略整体下线。
  • 全市场撤单(Cancel All):撤销当前交易账户下所有未成交订单,是 Kill Switch 最常用的动作。
  • 撤单并禁止新开(Cancel & Block):撤单同时冻结下单权限,防止策略在撤单后继续发单,属于“撤单+熔断”组合。
在 Kill Switch 设计中,建议默认使用“撤单并禁止新开”,因为单纯撤单后策略可能立即重新下单,导致撤单与下单形成竞态。

二、撤单优先级:谁先被撤掉

当系统需要在极短时间内撤销大量订单时,撤单顺序直接影响风险暴露。交易所和柜台通常不会保证“先发先撤”,因此需要在本地维护撤单优先级。常见优先级策略如下:

优先级 订单特征 理由
P0 已部分成交且剩余量大 剩余敞口最大,继续成交风险最高
P1 距离盘口最近(买一/卖一) 最容易被立即成交,撤单窗口最短
P2 大单、冰山单、算法子单 单笔影响大,撤单后对市场冲击明显
P3 远价挂单、长期未成交 成交概率低,可最后处理

在代码层面,撤单优先级通常体现为排序键。例如按“剩余量降序 + 价格距离升序”生成撤单队列:

def cancel_priority(order):
    # 剩余量越大越优先,价格越接近盘口越优先
    return (-order.remaining_qty, abs(order.price - order.mid_price))

cancel_queue = sorted(open_orders, key=cancel_priority)
for order in cancel_queue:
    cancel(order.order_id)

三、撤单延迟:从点击到确认的完整链路

撤单延迟不是单一数值,而是多个环节的累加。理解每一段延迟,才能定位“为什么撤不掉”。

紧急撤单延迟链路分解 策略/风控 触发撤单 交易网关 排队/序列化 柜台/API 网络+鉴权 交易所 撮合引擎 撤单确认 回报更新 0~1ms 1~5ms 5~50ms 1~20ms 回报延迟 关键延迟来源: ① 网关排队:撤单请求与下单请求共用通道,高峰期可能被阻塞 ② 网络抖动:跨机房、跨境链路 RTT 波动可达数十毫秒 ③ 交易所限流:撤单频率超限会被拒绝或延迟处理

从图中可以看到,撤单延迟的主要不确定性来自网关排队、网络抖动和交易所限流。在 Kill Switch 场景中,必须假设最坏情况下的延迟,而不是平均延迟。

四、撤单失败原因与影响

撤单失败并不罕见,尤其在极端行情下。常见原因包括:

  • 订单已成交:撤单请求到达时订单已完全成交,返回“订单不存在”或“已成交”。
  • 订单已撤销:重复撤单,交易所返回“已撤销”状态。
  • 撤单频率超限:交易所对撤单有每秒次数限制,超限后请求被拒绝。
  • 会话失效:API 过期、连接断开,撤单请求无法送达。
  • 合约状态异常:如合约进入交割、停牌或临时风控状态,禁止撤单。
  • 本地状态不一致:本地认为订单未成交,但交易所已成交,导致撤单逻辑误判。
撤单失败最危险的后果不是“没撤掉”,而是“系统以为撤掉了”。如果本地状态未与交易所对齐,Kill Switch 可能提前解除熔断,导致策略继续发单,形成二次风险。

因此,撤单后必须等待交易所回报确认,而不是以“请求已发送”作为成功依据。建议在 Kill Switch 中引入撤单确认超时机制:

cancel_all()
wait_until(timeout=3s, condition=all_orders_terminal)
if not all_orders_terminal:
    escalate_to_hard_kill()  # 强制断网、冻结账户、人工介入

五、案例复盘:一次撤单延迟导致的敞口扩大

某量化团队在夜盘突发行情中触发 Kill Switch,系统按优先级批量撤单。但由于网关排队,前 200ms 内仅撤掉 30% 订单,剩余订单在行情快速波动中继续成交,最终敞口比预期扩大 2.3 倍。复盘发现两个问题:一是撤单队列未按“盘口距离”排序,远价单先被处理;二是未设置撤单确认超时,系统在未确认全部撤单成功的情况下就解除了新开限制。

改进措施包括:将撤单优先级改为“盘口距离优先”,增加撤单确认超时与升级路径,并在 Kill Switch 触发后立即冻结策略下单权限,直到所有订单进入终态。这一案例说明,紧急撤单的核心不仅是“发出去”,更是“确认撤掉”和“防止再发”。

记住三个关键原则:撤单优先级要按风险暴露排序;撤单延迟要按最坏情况设计;撤单成功要以交易所终态回报为准。