订单生命周期:订单创建、路由、成交、撤销、过期处理

订单的生命周期,说白了就是一笔订单从“想法”到“结束”的全过程。我做了这么多年量化系统,见过太多人只关心策略信号,却忽略了订单本身的状态机。嗯,这里要敲黑板——订单生命周期管理,是交易系统稳定性的基石。

你想想看,一个订单从客户端发出,到最终被交易所确认,中间要经历多少个环节?每个环节都可能出问题。我个人习惯是把订单生命周期拆成五个核心阶段:创建、路由、成交、撤销、过期。下面我一个一个说。

1. 订单创建:一切从这里开始

订单创建,就是系统根据策略信号生成一个标准化的订单对象。我在项目中遇到过一个问题:策略端和交易端对订单字段的定义不一致,导致订单创建后直接被风控拦截。后来我强制要求所有订单必须经过一个统一的“订单工厂”来创建。

一个标准的订单对象,至少包含以下字段:

字段 说明 示例
订单ID 全局唯一标识 ORD-20231027-001
交易对 买卖标的 BTC/USDT
方向 买/卖 BUY
类型 限价/市价 LIMIT
价格 限价单的指定价格 35000.00
数量 委托数量 0.5
时间戳 创建时间(纳秒级) 1698384000000000
核心要点:订单ID必须全局唯一,且要包含时间信息。我习惯用“时间戳+自增序列+机器ID”的方式生成,这样既能保证唯一性,又能快速定位问题。

2. 订单路由:找到最快的路

订单创建好了,接下来要把它送到交易所。但问题来了——如果你对接了多个交易所,或者同一个交易所的不同节点,该往哪送?

订单路由的核心逻辑就两点:

  • 选所:根据流动性、手续费、延迟等因素,选择最优的交易所
  • 选路:同一个交易所可能有多个接入点,选延迟最低的那个

我曾经踩过一个坑:某个交易所的API网关做了限流,我所有的订单都走同一个连接,结果高峰期被限流了。后来我改成了多连接池+健康检查,每个连接独立计数,超过阈值自动切换。

我的建议:路由层一定要做“熔断”机制。如果某个交易所连续3次超时,自动切换到备用交易所,别死磕。

3. 订单成交:最激动人心的时刻

订单送到交易所后,就进入了撮合队列。成交有两种情况:

  • 完全成交:订单全部被撮合,状态变为FILLED
  • 部分成交:只成交了一部分,状态变为PARTIALLY_FILLED

这里有个容易被忽略的点:成交回报的顺序。交易所返回的成交回报,不一定和订单发送的顺序一致。我见过一个系统,因为没处理好成交回报的乱序问题,导致持仓计算错误。

我的做法是:在订单对象里维护一个“已成交数量”字段,每次收到成交回报就累加。同时记录一个“成交序列号”,用来判断是否漏掉了某笔成交。

// 伪代码:成交回报处理
class Order {
    long orderId;
    long filledQty;      // 已成交数量
    long lastSeqNum;     // 上次处理的成交序号
    
    void onFill(FillReport fill) {
        if (fill.seqNum <= this.lastSeqNum) {
            // 重复回报,直接丢弃
            return;
        }
        this.filledQty += fill.qty;
        this.lastSeqNum = fill.seqNum;
        
        if (this.filledQty >= this.totalQty) {
            this.status = OrderStatus.FILLED;
        } else {
            this.status = OrderStatus.PARTIALLY_FILLED;
        }
    }
}

4. 订单撤销:想撤就撤?没那么简单

撤销操作,看起来简单,其实坑很多。你发一个撤销指令,交易所不一定能成功撤销。比如:

  • 订单已经成交了,撤销会失败
  • 订单正在撮合中,撤销可能被延迟
  • 网络超时,你不知道撤销到底成功了没有
注意:撤销操作一定要做“确认-重试”机制。我曾经因为撤销超时后直接放弃,结果订单在5秒后成交了,导致仓位超限。后来我改成:撤销超时后,先查询订单状态,如果还是“待成交”,再重试撤销。

我个人习惯的撤销流程:

  1. 发送撤销指令
  2. 等待交易所确认(超时时间设为500ms)
  3. 如果超时,立即查询订单状态
  4. 根据状态决定是否重试撤销
  5. 最多重试3次,超过则标记为“撤销待确认”

5. 过期处理:没人管的订单怎么办

有些订单设置了有效期,比如“当天有效”或“30秒有效”。过期后,系统需要自动处理。这里有两种策略:

策略 做法 适用场景
主动撤销 过期后立即发送撤销指令 高频交易,对时效性要求高
被动等待 过期后不操作,等交易所自动撤销 低频交易,减少网络开销

我建议用主动撤销。为什么?因为交易所的自动撤销机制不一定可靠。我遇到过某个交易所,订单过期后过了10秒才自动撤销,这期间价格已经变了,差点造成损失。

避坑指南:过期处理一定要有一个“定时扫描”任务。我通常每100ms扫描一次所有未成交订单,检查是否过期。扫描间隔不能太长,否则订单过期了你还不知道。

核心逻辑流程图

下面这张图,展示了订单从创建到结束的完整状态流转。我画这张图的时候,特意把“异常路径”也标出来了——因为实际生产中,异常路径才是你真正需要关注的。

订单生命周期状态流转图 订单创建 路由中 待成交 部分成交 完全成交 已撤销 已过期 订单结束 继续撮合 创建 路由 待成交 部分成交 完全成交 撤销 过期

从图上你能看到,订单的生命周期其实是一个有向图,不是简单的线性流程。每个状态之间都有多条路径。比如“待成交”状态,可以走向“部分成交”、“已撤销”或“已过期”。而“部分成交”之后,还可能回到“待成交”继续撮合。

嗯,这就是订单生命周期的全部内容。说白了,就是管理好订单的每一个状态转换,确保不丢单、不重复、不错乱。我在实战中吃过不少亏,希望你能从我的经验里少走些弯路。


专注资料整理