第四章:订单管理与路由系统
订单管理与路由系统,说白了就是交易系统的「交通指挥中心」。我个人习惯把它看作三个核心模块:订单状态机、订单簿管理、智能路由。再加上一道风控前置检查,这四块构成了整个交易执行的核心骨架。
嗯,咱们一个一个来拆解。
4.1 订单状态机设计
订单状态机,是订单从生到死的完整生命周期管理。我见过不少新手直接用一个字符串字段存状态,结果上线后各种状态混乱,对账对到崩溃。
一个标准的订单状态机,至少包含以下状态:
| 状态 | 含义 | 触发条件 |
|---|---|---|
| PENDING_NEW | 待提交 | 用户下单 |
| NEW | 已提交 | 交易所确认接收 |
| PARTIALLY_FILLED | 部分成交 | 部分订单被撮合 |
| FILLED | 全部成交 | 订单完全撮合 |
| CANCELED | 已撤销 | 用户主动撤单 |
| REJECTED | 已拒绝 | 风控或交易所拒绝 |
你想想看,如果状态机设计得不够严谨,会出现什么情况?比如订单已经全部成交了,还能收到撤单指令,那系统就乱套了。
我个人习惯用有限状态机(FSM)来实现,代码结构清晰,也方便后续扩展。下面是一个简单的状态机核心逻辑:
public enum OrderStatus {
PENDING_NEW, NEW, PARTIALLY_FILLED,
FILLED, CANCELED, REJECTED
}
public class OrderStateMachine {
private OrderStatus currentStatus;
// 状态转换规则表
private static final Map<OrderStatus, Set<OrderStatus>> transitions =
Map.of(
OrderStatus.PENDING_NEW, Set.of(OrderStatus.NEW, OrderStatus.REJECTED),
OrderStatus.NEW, Set.of(OrderStatus.PARTIALLY_FILLED, OrderStatus.FILLED, OrderStatus.CANCELED),
OrderStatus.PARTIALLY_FILLED, Set.of(OrderStatus.FILLED, OrderStatus.CANCELED)
);
public boolean canTransitionTo(OrderStatus target) {
return transitions.getOrDefault(currentStatus, Set.of()).contains(target);
}
}
4.2 订单簿管理
订单簿,就是买卖双方的挂单队列。说白了,它就是个价格优先、时间优先的排序结构。
我在项目中遇到过一个问题:订单簿深度更新不及时,导致策略看到的盘口和实际盘口差了几个tick。结果策略以为能成交,实际根本吃不到单子。
订单簿的核心数据结构其实不复杂:
- 买盘(Bid): 按价格从高到低排序
- 卖盘(Ask): 按价格从低到高排序
- 每个价格档位: 记录总数量和订单列表
这里我画了一张订单簿的结构图,方便你理解:
订单簿的更新逻辑,我建议用增量更新+全量快照的双重机制。平时只处理增量数据,每隔一段时间做一次全量快照做校验。这样既能保证实时性,又能防止数据累积误差。
4.3 智能路由算法
智能路由,就是帮你的订单找到最优的交易所。现在很多品种都在多个交易所同时交易,同一个价格在不同交易所可能差好几个tick。
为什么会这样?因为每个交易所的流动性深度不一样,撮合速度也不一样。你想想看,如果一个大单子全部砸在一个交易所,可能直接把盘口打穿好几个档位。但拆成小单子分散到多个交易所,成交成本就低很多。
我常用的路由策略有几种:
- 价格优先路由: 哪个交易所价格好,就往哪送
- 流动性路由: 按各交易所的深度比例分配订单
- 延迟敏感路由: 优先选择网络延迟最低的交易所
核心算法示例: 基于流动性的加权路由
public class SmartRouter {
// 计算各交易所的流动性权重
public Map<Exchange, Double> calculateWeights(OrderBookSnapshot snapshot) {
double totalLiquidity = snapshot.getExchanges().stream()
.mapToDouble(e -> e.getBidDepth(5) + e.getAskDepth(5))
.sum();
Map<Exchange, Double> weights = new HashMap<>();
for (Exchange ex : snapshot.getExchanges()) {
double depth = ex.getBidDepth(5) + ex.getAskDepth(5);
weights.put(ex, depth / totalLiquidity);
}
return weights;
}
}
嗯,这里要注意一点:路由算法不能只看当前状态,还要考虑历史表现。我记得有一次某个交易所突然网络抖动,但流动性数据还没更新,结果路由算法继续往那送单,白白损失了好几笔。
4.4 风控前置检查
风控前置检查,是订单进入交易系统前的最后一道防线。说白了,就是「先检查,再执行」。我见过太多因为风控后置导致爆仓的案例了。
我个人习惯把风控检查分成三个层级:
| 层级 | 检查项 | 说明 |
|---|---|---|
| L1 基础检查 | 订单参数合法性 | 价格、数量、方向是否合规 |
| L2 资金检查 | 可用资金、持仓限额 | 防止超仓、透支交易 |
| L3 策略检查 | 频率限制、自成交防护 | 防止高频报单、自成交 |
风控检查的代码实现,我建议用责任链模式。每个检查项是一个独立的处理器,可以灵活组合和扩展:
public interface RiskCheckHandler {
boolean check(Order order);
void setNext(RiskCheckHandler next);
}
public class RiskCheckChain {
private List<RiskCheckHandler> handlers = new ArrayList<>();
public boolean execute(Order order) {
for (RiskCheckHandler handler : handlers) {
if (!handler.check(order)) {
// 记录风控日志
log.warn("风控拦截: {} 未通过 {}", order.getId(), handler.getClass().getSimpleName());
return false;
}
}
return true;
}
}
好了,订单管理与路由系统的核心内容就这些。状态机管生命周期,订单簿管市场数据,路由算法管执行路径,风控检查管安全底线。四者配合好了,交易系统才能跑得稳、跑得快。
公众号:蓝海资料掘金营,微信deep3321