第十一章:做市商系统架构
做市商系统,说白了就是一台「抢钱机器」。
我入行那会儿,带我的老大哥说过一句话,我一直记着:「你的系统比别人慢1毫秒,你就得比别人多亏1%的利润。」这话听着夸张,但干过这行的都懂——在高度竞争的做市领域,延迟就是真金白银。
这一章,咱们聊聊系统架构的核心。不扯虚的,全是实战中摸爬滚打出来的经验。
11.1 低延迟系统设计:从网线到成交的每一微秒
先问个问题:一笔订单从你的策略发出,到交易所确认成交,中间经历了什么?
我拆解给你看:
- 策略计算(CPU处理)
- 内存拷贝(数据从用户态到内核态)
- 网卡发送(协议栈封装)
- 网络传输(光纤/微波)
- 交易所撮合引擎处理
- 回包接收(同样的路径反向走一遍)
每一步都有延迟。我见过太多团队,花大价钱优化了策略,结果发现瓶颈在网卡驱动上。
核心原则:延迟优化的目标是「确定性」,而不是「快」。稳定在10微秒,比偶尔1微秒、偶尔100微秒要强得多。
11.1.1 硬件选型
我个人习惯,做市商服务器首选Intel Xeon系列,主频要高,核心数反而不是关键。为什么?因为做市策略通常是单线程的,多核并行反而引入锁竞争。
内存呢?DDR5?别急着上。我踩过坑——DDR5延迟比DDR4高,虽然带宽大,但做市商要的是低延迟,不是高吞吐。
| 组件 | 推荐配置 | 避坑点 |
|---|---|---|
| CPU | Intel Xeon W-3175X / AMD EPYC 7F72 | 别选低主频的服务器U |
| 内存 | DDR4-3200 CL14 | DDR5延迟更高,慎用 |
| 网卡 | Solarflare X2522 / Mellanox ConnectX-6 | 必须支持用户态协议栈 |
| 硬盘 | Intel Optane P5800X | NVMe SSD延迟还是高 |
小技巧:BIOS里关闭超线程、关闭CPU节能模式、关闭C-State。这些设置能稳定降低20%-30%的延迟抖动。
11.1.2 软件优化
操作系统层面,我推荐用Linux内核的RT(实时)补丁,或者干脆用DPDK绕过内核。你想想看,每次系统调用都要上下文切换,这成本太高了。
代码层面,注意几点:
- 避免动态内存分配(用对象池)
- 避免锁(用无锁队列)
- 避免虚函数(减少间接跳转)
- 数据对齐到缓存行(64字节)
// 伪代码:无锁队列示例
template<typename T>
class LockFreeQueue {
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
T* buffer_;
public:
bool push(const T& item) {
size_t tail = tail_.load(std::memory_order_relaxed);
size_t next = (tail + 1) % capacity_;
if (next == head_.load(std::memory_order_acquire))
return false; // 队列满
buffer_[tail] = item;
tail_.store(next, std::memory_order_release);
return true;
}
};
注意:无锁队列不是银弹。我曾经在一个项目里用了复杂的无锁结构,结果调试了整整两周才发现一个ABA问题。能用简单锁的地方,别硬上无锁。
11.2 事件驱动架构:让系统「活」起来
做市商系统本质上是一个事件处理机。行情来了、订单成交了、风控触发了——这些都是事件。
我习惯把系统设计成「事件总线」模式。所有模块只跟总线通信,不直接调用对方。好处是解耦,坏处是……嗯,调试起来有点麻烦。
11.2.1 事件类型
- 行情事件:Tick数据、OrderBook快照
- 交易事件:订单确认、成交回报、撤单结果
- 风控事件:仓位超限、资金不足、异常波动
- 系统事件:心跳超时、连接断开、重连成功
11.2.2 事件处理流程
我画个图,你一看就明白:
你看,所有模块都挂在总线上。行情来了,总线广播给所有订阅者。策略引擎收到后计算新价格,然后发一个「下单事件」回到总线,订单管理器再处理。
实战经验:事件总线一定要做「优先级队列」。风控事件优先级最高,必须立即处理。我见过一个系统,因为行情事件太多,风控事件被堵在后面,结果爆仓了。
11.3 内存数据库应用:告别磁盘IO
做市商系统里,磁盘IO是敌人。一次磁盘读写几毫秒,够行情跳好几个价位了。
所以,所有数据都放内存。订单簿、持仓、资金、策略参数——全在内存里。
11.3.1 选型对比
| 方案 | 延迟 | 持久化 | 适用场景 |
|---|---|---|---|
| Redis | ~100μs | 支持(AOF/RDB) | 策略参数、配置信息 |
| Aerospike | ~50μs | 支持(SSD持久化) | 用户数据、历史统计 |
| 自研内存DB | <1μs | 需自行实现 | 订单簿、持仓等核心数据 |
我个人倾向自研。为什么?因为Redis再快,也有网络开销。你想想看,每次查询都要走TCP/IP,这延迟受不了。
// 自研内存订单簿示例
class OrderBook {
std::unordered_map<uint64_t, Order> orders_;
std::map<double, std::vector<Order*>, std::greater<double>> bids_;
std::map<double, std::vector<Order*>> asks_;
public:
void add_order(const Order& order) {
orders_[order.id] = order;
if (order.side == BUY) {
bids_[order.price].push_back(&orders_[order.id]);
} else {
asks_[order.price].push_back(&orders_[order.id]);
}
}
// 查询最优买卖价 O(1)
double best_bid() { return bids_.empty() ? 0 : bids_.begin()->first; }
double best_ask() { return asks_.empty() ? 0 : asks_.begin()->first; }
};
注意:内存数据库虽然快,但一断电数据就没了。我建议做「双写」——内存里一份,同时异步写一份到Redis。这样既保证速度,又保证数据不丢。
11.4 FPGA加速基础:硬件级的极致优化
说到FPGA,很多人觉得这是「黑科技」。其实没那么神秘,说白了就是用硬件电路来实现你的逻辑。
我为什么推荐FPGA?因为CPU是顺序执行的,一条指令一条指令跑。而FPGA是并行执行的,一个时钟周期能干好多事。
11.4.1 适合FPGA的场景
- 行情解析:交易所的二进制协议解析,FPGA可以做到纳秒级
- 订单簿重建:增量更新,FPGA流水线处理
- 风控检查:价格校验、数量校验,硬件级过滤
- 信号生成:简单的策略逻辑(如价差套利)
11.4.2 一个简单的例子
假设你要做行情解析。交易所发来的数据是二进制格式,你需要解析出价格、数量、方向。
CPU做法:
// 逐字节解析
uint64_t price = (buffer[0] << 56) | (buffer[1] << 48) | ...;
FPGA做法:
// Verilog代码片段
reg [63:0] price;
always @(posedge clk) begin
price <= {data_in[63:56], data_in[55:48], data_in[47:40],
data_in[39:32], data_in[31:24], data_in[23:16],
data_in[15:8], data_in[7:0]};
end
你看,FPGA一个时钟周期就搞定了。CPU要好几条指令,还要考虑字节序。
入门建议:别一上来就搞复杂的。先从「行情解析」开始,用FPGA把行情数据解析好,然后通过PCIe传给CPU。这样CPU只负责策略计算,延迟能降一个数量级。
11.4.3 开发流程
- 用C/C++写算法原型
- 用HLS(高层次综合)转成Verilog
- 仿真验证
- 烧录到FPGA板卡
- 联调测试
避坑指南:我曾经花了一个月写了一个复杂的FPGA逻辑,结果仿真全对,上板就挂。最后发现是时序约束没写对。记住:FPGA开发,时序约束比逻辑本身更重要。
小结
这一章内容不少,但核心就一句话:做市商系统,延迟就是生命。
从硬件选型到软件架构,从事件驱动到FPGA加速,每一步都在跟时间赛跑。我见过太多团队,策略很牛,但系统跑不起来,最后被市场淘汰。
记住:系统架构不是锦上添花,是生存之本。
交易系统化学习资料 微信Strategy888888