第二章:订单生命周期——从客户端到撮合引擎的完整路径

大家好,我是你们的讲师。今天我们来聊聊订单的生命周期。

说白了,一个订单从你敲下回车键,到最终被撮合或者取消,中间经历的事情比你想象的多得多。我当年刚接触期货交易系统时,以为订单就是“发出去→成交”这么简单。直到有一次生产环境出了bug,订单状态卡在“PartiallyFilled”整整三分钟……嗯,那天的复盘会开了四个小时。

2.1 订单的“出生”:从客户端到网关

订单的旅程,从客户端开始。

你写了一个买入开仓指令,点击提交。这个指令首先被序列化成FIX协议或者内部二进制协议,然后通过网络发出去。这里有个细节——客户端不会直接把订单发给撮合引擎。中间至少隔着一层网关(Gateway)。

典型路径:

客户端 → 前置网关 → 风控校验 → 订单路由 → 撮合引擎

网关的作用是什么?我个人的理解是:挡掉垃圾流量。比如你发了个格式错误的订单,网关直接返回Rejected,根本不会让撮合引擎知道。这就像公司前台,先帮你筛掉推销电话。

2.2 风控校验:订单的“体检”

订单到了网关,第一件事是风控校验。这一步非常关键。

我曾经见过一个案例:某个客户写了个死循环脚本,每秒发1000个订单。如果没有风控,撮合引擎直接被打挂。所以网关会检查:

  • 资金是否足够——保证金不够?Rejected。
  • 持仓是否超限——超过持仓限额?Rejected。
  • 价格是否合理——超出涨跌停板?Rejected。
  • 频率是否过高——每秒超过N笔?Rejected。

你想想看,如果这些校验都放到撮合引擎里做,那引擎的负担就太重了。所以前置风控是必须的

2.3 订单状态机:New、PartiallyFilled、Filled、Cancelled、Rejected

好,订单通过了风控,到达撮合引擎。这时候它是什么状态?

New——刚进入撮合队列,还没被处理。这是订单的“初始状态”。

然后撮合引擎开始干活。它把订单和对手盘进行匹配。匹配结果有三种可能:

状态 含义 触发条件
Filled 全部成交 订单数量完全匹配到对手盘
PartiallyFilled 部分成交 只匹配了一部分,剩余部分继续挂单
Cancelled 已撤销 用户主动取消,或超时自动取消
Rejected 已拒绝 风控校验失败、格式错误、系统内部错误

这里有个容易踩坑的地方:PartiallyFilled 之后还能继续成交。比如你挂了10手,先成交了3手,状态变成PartiallyFilled。剩下的7手还在队列里,等对手盘来了继续成交。直到全部成交,状态才变成Filled。

注意:PartiallyFilled 状态不是终态。它只是一个中间状态。系统必须能处理“部分成交后继续撮合”的场景。我曾经见过一个系统,PartiallyFilled之后直接跳到了Filled,把剩余订单丢了……那是个严重的bug。

2.4 状态转换图

为了更直观地理解,我画了一张状态机图。你仔细看:

New PartiallyFilled Filled Cancelled Rejected 部分成交 全部成交 校验失败 剩余成交 主动取消 主动取消 图例说明: 初始状态 中间状态 终态(成功) 终态(失败) 终态(拒绝)

这张图我建议你保存下来。每次设计订单系统时,对照着看,不容易漏状态。

2.5 订单的“死亡”:终态处理

订单到达终态后,系统需要做什么?

  • Filled:生成成交记录,更新持仓和资金。通知客户端“你买到了”。
  • Cancelled:从撮合队列中移除。如果已经部分成交,剩余部分取消,已成交部分不变。
  • Rejected:记录错误原因。通知客户端“订单被拒,原因是XXX”。

这里有个细节:Rejected 和 Cancelled 的区别。Rejected 是系统主动拒绝,比如资金不足。Cancelled 是用户主动取消。两者虽然都是“没成交”,但业务含义完全不同。风控系统会重点监控 Rejected 订单,因为可能意味着用户操作异常。

个人经验:我建议在订单表里加一个 reject_reason 字段。哪怕只是字符串,调试时能省你半天时间。我曾经因为没加这个字段,查一个Rejected订单的原因查了三个小时……后来发现是价格精度不对。

2.6 实战中的坑

最后分享几个我踩过的坑:

  1. 状态丢失:订单从PartiallyFilled变成Filled时,如果数据库写入失败,订单就卡住了。解决方案是加一个“状态变更日志表”,每次变更都写日志,方便恢复。
  2. 重复成交:撮合引擎处理完订单后,如果网络超时,客户端可能重发。这时候要保证幂等性——同一个订单ID只能被处理一次。
  3. 取消延迟:用户发取消指令时,订单可能正在撮合中。这时候要加锁,或者用“取消标记”的方式,等撮合完再处理取消。

嗯,今天就讲到这里。订单生命周期是撮合引擎的基础,理解了它,后面的内容就好办了。


专注资料整理