第二章:系统架构设计——分层架构设计、核心组件介绍、数据流与订单流
好,咱们开始聊架构。说实话,很多做高频交易的朋友,一上来就盯着延迟优化、盯着纳秒级比拼。但我的经验是——架构没搭好,后面全是坑。我在第一家公司做量化系统时,就因为分层不清晰,一个行情模块的bug直接带崩了整个交易链路,那叫一个惨。
今天这一章,我就把高频交易系统的骨架给你拆开看。你想想看,一栋大楼要盖得高,地基和框架得先稳。交易系统也一样。
一、为什么必须分层?
高频交易系统,说白了就是跟时间赛跑。但跑得快的前提是——每个模块各司其职。
我个人习惯把系统分成四层:
- 接入层:负责跟交易所握手,收行情、发订单
- 逻辑层:策略计算、信号生成、风险管理
- 执行层:订单管理、路由、撮合
- 数据层:存储、回放、分析
为什么要这么分?我举个例子。假设你的策略逻辑里有个死循环,如果它跟行情接收模块混在一起,那行情数据就断了,整个系统就瞎了。分层之后,逻辑层崩了,接入层还能继续收行情,数据层还能把数据存下来。等逻辑层重启,还能从数据层补回这段时间的行情。这叫故障隔离。
核心原则:每一层只做一件事,并且把这件事做到极致。层与层之间通过明确定义的接口通信,绝不越界。
二、核心组件逐个拆解
好,咱们一层一层往下看。每个层里都有哪些关键组件?
1. 接入层——行情网关 & 交易网关
这是系统的最前线。行情网关负责接收交易所的行情数据,交易网关负责发送订单和接收成交回报。
这里有个坑,我踩过。交易所的行情协议,比如CTP、FIX/FAST,数据格式都不一样。有的用二进制,有的用XML。你如果每个交易所都写一套解析代码,那维护成本就上天了。
我的做法是:在接入层内部做一个统一的内部行情格式。不管外面来的是什么协议,进到系统内部都转成同一种结构体。这样上层策略就不用关心底层是哪个交易所了。
// 内部统一行情结构体(C++示例)
struct MarketData {
uint64_t timestamp; // 纳秒时间戳
char symbol[16]; // 合约代码
double bid_price; // 买一价
double ask_price; // 卖一价
uint32_t bid_volume; // 买一量
uint32_t ask_volume; // 卖一量
uint8_t exchange_id; // 交易所编号
};
避坑指南:我曾经因为行情网关里用了动态内存分配(new/delete),导致在高频行情下出现微秒级的抖动。后来全部改成预分配内存池,延迟稳定多了。记住,高频场景下,零动态分配是基本要求。
2. 逻辑层——策略引擎 & 风控模块
策略引擎是大脑。它拿到行情数据后,根据你的交易策略(比如做市、套利、趋势跟踪)计算出买卖信号。
但这里有个关键点——策略引擎不能直接下单。它算出来的信号,要先经过风控模块的检查。
风控模块做什么?我列几个常见的:
- 资金检查:账户余额够不够?
- 仓位检查:会不会超仓?
- 频率检查:每秒下单次数有没有超过限制?
- 价格检查:下单价格是不是明显偏离市场?
嗯,这里要注意。风控模块如果做得太重,会拖慢整个链路。我建议把风控分成两级:快速风控和深度风控。快速风控只做最简单的检查(比如价格偏离),放在逻辑层内部。深度风控(比如资金、仓位)放在执行层,异步处理。
3. 执行层——订单管理器 & 路由引擎
订单管理器(Order Manager)是执行层的核心。它负责管理每一个订单的完整生命周期:
| 状态 | 说明 |
|---|---|
| PENDING_NEW | 订单已生成,等待发送 |
| NEW | 已发送到交易所,等待确认 |
| PARTIALLY_FILLED | 部分成交 |
| FILLED | 全部成交 |
| CANCELED | 已撤销 |
| REJECTED | 被交易所拒绝 |
路由引擎呢?它负责决定订单发到哪个交易所。如果你同时连了上期所、大商所、郑商所,那路由引擎就要根据流动性、延迟、手续费等因素,选择最优的交易所。
注意:订单管理器必须保证幂等性。什么意思?就是同一个订单,不管重发多少次,结果都一样。否则网络抖动时,你可能会重复下单,造成严重损失。我见过有人因为这个,一天亏了十几万。
4. 数据层——存储 & 回放
数据层看起来不起眼,但它是整个系统的记忆体。没有它,你就没法做回测,也没法复盘。
高频交易的数据量有多大?我给你算一笔账。一个期货合约,每秒可能有几百笔行情。一天下来,光一个合约就是几百万条数据。如果你做全市场,一天的数据量轻松上GB。
所以存储方案很关键。我推荐用列式存储或者时序数据库,比如InfluxDB、ClickHouse。它们对时间序列数据的压缩和查询效率远高于传统关系型数据库。
三、数据流与订单流——系统里的血液
架构搭好了,组件也认识了。接下来咱们看看数据是怎么在系统里流动的。我画了一张图,帮你直观理解。
这张图里,我用了三种颜色的箭头:
- 绿色箭头:行情数据流。从上往下走,交易所 -> 接入层 -> 逻辑层。这是单向的,只读不写。
- 红色箭头:订单流。从下往上走,逻辑层 -> 执行层 -> 接入层 -> 交易所。这是双向的,因为订单发出去后,还有成交回报回来。
- 蓝色虚线箭头:数据持久化与回放。每一层产生的数据,都可以异步写入数据层。回测时,数据层再把历史数据喂给逻辑层。
你可能会问:为什么订单流和行情流要分开?原因很简单——优先级不同。行情数据量大,但丢了可以重传;订单数据量小,但一条都不能丢。所以一般会用不同的网络通道和线程来处理。
关键设计原则:数据流是推模式(push),订单流是拉模式(pull)。行情来了就推给上层,订单则是由上层主动发起。这样设计,能最大程度减少不必要的等待。
四、避坑总结
最后,我把自己这些年踩过的坑,总结成几条原则,你记一下:
- 分层要彻底:层与层之间只通过接口通信,不要共享内存或全局变量。否则一个bug能传染整个系统。
- 异步为王:所有IO操作(网络、磁盘)都要异步。同步等待在高频场景下是灾难。
- 监控先行:每一层都要有监控指标——延迟、吞吐量、错误率。没有监控,你就像在黑暗中开车。
- 可回放:所有数据都要能回放。这样出了问题,你可以像看录像一样,一帧一帧地查。
嗯,架构设计就聊到这儿。记住一句话:好的架构,是让错误的影响最小化,而不是让错误不发生。因为在高频交易里,错误是不可避免的,关键是你怎么兜底。