在高频交易(HFT)、做市(Market Making)和统计套利策略中,本地订单簿(Local Order Book) 是几乎所有决策的基础。
它实时反映市场深度(Depth)、买卖压力(Imbalance)、队列位置(Queue Position)等关键信息。没有准确、低延迟的订单簿视图,策略就失去了最核心的“市场微观结构”感知能力。
现代交易所(如Binance、CME、Nasdaq等)通常采用 “全量快照 + 增量更新(Delta)” 的推送模式:
本地系统必须:
Rust凭借零成本抽象、极致性能和内存安全,成为实现微秒级(甚至亚微秒级)订单簿重建的首选语言。
最基础的L2订单簿(Market-by-Price,价格聚合)结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use std::collections::HashMap;
/// 买卖方向
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Side {
Buy, // Bid
Sell, // Ask
}
/// 单价格档位(Price Level)
#[derive(Debug, Clone)]
pub struct OrderLevel {
pub price: f64, // 注意:实际生产中建议用定点数或整数表示
pub volume: u64, // 累计委托量
pub order_count: u32, // 委托笔数(部分交易所提供)
}
/// 本地订单簿(L2,10档或20档常见)
#[derive(Debug)]
pub struct OrderBook {
// 推荐使用 BTreeMap 而非 HashMap
// 原因:需要按价格排序(买降序、卖升序),BTreeMap天然有序,范围查询O(log n)
// HashMap无序,获取top-N需要额外排序,性能更差
bids: BTreeMap<PriceKey, OrderLevel>, // 买单:价格从高到低
asks: BTreeMap<PriceKey, OrderLevel>, // 卖单:价格从低到高
// 辅助:记录当前序列号,用于检测丢包
last_seq: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PriceKey(i64); // 关键优化:避免浮点数作为key!
impl PriceKey {
// 假设价格精度为0.0001 → 乘10000转为整数
pub fn from_float(p: f64) -> Self {
Self((p * 10_000.0).round() as i64)
}
pub fn to_float(&self) -> f64 {
self.0 as f64 / 10_000.0
}
}
交易所通常推送两种数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
impl OrderBook {
pub fn apply_snapshot(&mut self, snapshot: &Snapshot) {
self.bids.clear();
self.asks.clear();
for level in &snapshot.bids {
self.bids.insert(level.price, level.clone());
}
for level in &snapshot.asks {
self.asks.insert(level.price, level.clone());
}
}
pub fn apply_incremental(&mut self, update: &IncrementalUpdate) {
for delta in &update.deltas {
match delta.side {
Side::Buy => {
if delta.volume == 0 {
self.bids.remove(&delta.price);
} else {
self.bids.insert(delta.price, OrderLevel {
price: delta.price,
volume: delta.volume,
order_count: delta.order_count,
});
}
}
Side::Sell => {
if delta.volume == 0 {
self.asks.remove(&delta.price);
} else {
self.asks.insert(delta.price, OrderLevel {
price: delta.price,
volume: delta.volume,
order_count: delta.order_count,
});
}
}
}
}
}
}
1
2
// 使用预分配的Vec
let mut bids: Vec<OrderLevel> = Vec::with_capacity(10);
1
2
3
4
// 直接解析二进制数据,避免String转换
fn parse_price(data: &[u8]) -> f64 {
f64::from_le_bytes(data[0..8].try_into().unwrap())
}
1
2
3
4
5
6
7
8
// 使用SIMD指令批量处理
use std::arch::asm;
unsafe {
asm!(
"addpd xmm0, xmm1",
);
}
大多数策略只关心前10~20档 → 维护固定大小的Vec或RingBuffer。
1
2
3
4
5
6
// 示例:只保留前20档
if self.bids.len() > 20 {
while self.bids.len() > 20 {
self.bids.pop_first();
}
}
1
2
3
4
5
6
7
// 初始化时预分配
let mut book = OrderBook {
bids: BTreeMap::new(),
asks: BTreeMap::new(),
last_seq: 0,
};
// 或使用 smallvec / arrayvec 对于极浅深度
把常用字段(如best bid/ask、spread、imbalance)缓存到结构体中,避免每次查询遍历。
订单簿重建是高频/低延迟系统的“地基工程”。
Rust的确定性性能、所有权模型和丰富生态(BTreeMap、smallvec、bytes等)让它在这一领域有天然优势。
真正拉开差距的不是代码本身,而是对数据协议的深刻理解、对边界case的完备处理以及持续的微基准测试与火焰图分析。
下一篇,高频策略的Rust内存优化:对象池与零分配设计模式
敬请期待。
(全文完)