第三章:订单管理模块设计
订单管理模块,说白了就是做市商系统的心脏。你想想看,所有策略的决策最终都要落地成订单,而订单怎么来、怎么走、怎么成交,全看这个模块的设计。我个人习惯把订单管理拆成四个核心部分:生命周期、数据结构、撮合引擎、路由策略。咱们一个一个聊。
3.1 订单生命周期:从生到死的完整轨迹
一个订单从创建到最终消亡,中间会经历好几个状态。我在项目中遇到过不少新手,上来就搞个「已提交」「已成交」「已撤销」三个状态完事——结果上线第一天就出问题。
为什么?因为真实场景里,订单可能部分成交、可能被拒绝、可能因风控被冻结。你想想看,状态机设计不严谨,资金对账就会乱套。
我建议至少定义以下状态:
| 状态 | 含义 | 触发条件 |
|---|---|---|
| Pending | 待处理 | 订单刚创建,尚未进入撮合队列 |
| Open | 已挂单 | 订单进入订单簿,等待撮合 |
| PartiallyFilled | 部分成交 | 撮合引擎匹配了部分数量 |
| Filled | 完全成交 | 剩余数量为0 |
| Cancelled | 已撤销 | 用户主动撤单或超时 |
| Rejected | 已拒绝 | 风控校验不通过或参数非法 |
3.2 订单簿数据结构:价格优先、时间优先
订单簿的核心就八个字:价格优先,时间优先。说白了,买单出价高的排前面,卖单出价低的排前面,价格一样就看谁先来。
数据结构怎么选?我推荐用跳表(Skip List)或者红黑树。为什么不用数组?因为订单簿需要频繁插入和删除,数组的 O(n) 复杂度扛不住高频场景。
下面是我常用的订单簿结构定义:
// 价格档位节点
struct PriceLevel {
double price; // 价格
uint64_t totalQty; // 该价格总挂单量
std::list<Order> orders; // 同价格下的订单队列(FIFO)
};
// 订单簿
class OrderBook {
std::map<double, PriceLevel, std::greater<>> bids; // 买单,价格降序
std::map<double, PriceLevel, std::less<>> asks; // 卖单,价格升序
std::unordered_map<uint64_t, Order> orderMap; // 订单ID快速查找
};
3.3 撮合引擎核心算法:锁与无锁之争
撮合引擎是性能瓶颈的重灾区。我见过两种主流方案:
- 单线程事件循环:简单可靠,没有锁竞争。适合大部分场景。
- 无锁队列 + 多线程:性能极致,但调试起来想哭。我建议非万不得已别碰。
核心算法其实不复杂:
void match(Order& takerOrder) {
while (takerOrder.remainingQty > 0) {
// 找到对手方最优价格
PriceLevel* bestLevel = (takerOrder.side == BUY)
? getBestAsk() : getBestBid();
if (!bestLevel || !isPriceMatch(takerOrder, bestLevel->price)) {
break; // 没有可撮合的订单
}
// 按FIFO顺序撮合
auto& orderList = bestLevel->orders;
while (!orderList.empty() && takerOrder.remainingQty > 0) {
Order& makerOrder = orderList.front();
uint64_t tradeQty = std::min(takerOrder.remainingQty,
makerOrder.remainingQty);
// 生成成交记录
generateTrade(takerOrder, makerOrder, tradeQty);
// 更新数量
takerOrder.remainingQty -= tradeQty;
makerOrder.remainingQty -= tradeQty;
if (makerOrder.remainingQty == 0) {
orderList.pop_front();
}
}
// 如果价格档位空了,清理掉
if (orderList.empty()) {
removePriceLevel(bestLevel);
}
}
}
3.4 订单路由策略:把订单送到该去的地方
做市商通常会在多个交易所同时挂单。订单路由就是决定「这个订单该发到哪个交易所」。我常用的策略有三种:
| 策略 | 适用场景 | 优缺点 |
|---|---|---|
| Smart Order Routing (SOR) | 流动性分散的场景 | 自动选择最优价格,但延迟较高 |
| 固定路由 | 主交易所流动性充足 | 延迟最低,但灵活性差 |
| 比例路由 | 需要分散风险 | 按比例分配,但可能错过最优价格 |
我个人习惯用 SOR 加一层缓存。为什么?因为每次路由都去查各交易所的实时行情,延迟太高。我会在本地维护一个「价格快照」,每 10 毫秒更新一次。路由时直接读快照,速度能快一个数量级。
3.5 整体架构图
下面这张图展示了订单管理模块的核心流程。从订单创建到最终成交或撤销,每一步都有对应的处理逻辑。
嗯,这张图把整个流程串起来了。你想想看,从订单创建开始,先过风控——这是第一道防线。过了风控才能进订单簿,然后撮合引擎开始工作。如果风控没通过,直接拒绝,不给任何机会。
好了,订单管理模块的核心内容就这些。记住,数据结构选对、状态机设计严谨、撮合算法高效、路由策略灵活,这四个点做好了,你的订单模块就稳了。