第二章 系统架构总览:分层架构设计、核心组件与数据流

好,咱们直接进入正题。这一章我打算聊聊高频交易系统的骨架——也就是整体架构长什么样。很多新手一上来就盯着策略代码猛写,结果行情来了,系统崩了,订单飞了。嗯,这种事我在早期项目里见过太多次了。

说白了,高频交易系统就是一个极致的实时数据处理管道。它要处理的事情其实不复杂:拿到行情,做出决策,执行交易。但难就难在,这一切必须在微秒甚至纳秒级别完成。

2.1 分层架构设计:为什么必须分层?

我个人习惯把系统拆成四层。你想想看,如果不分层,所有逻辑揉在一起,改一个地方可能引发连锁反应。我见过一个团队,因为行情解析和策略逻辑混在一起,换个交易所协议,整个系统重写了一遍——太痛了。

这四层分别是:

  • 接入层:负责跟交易所打交道。接收行情、发送订单。
  • 业务逻辑层:策略计算、订单管理、风控检查。
  • 数据层:存储历史数据、日志、配置。
  • 监控层:系统健康检查、延迟监控、告警。

每一层只做自己的事。接入层不关心策略怎么算,策略层不关心数据怎么存。这样出了问题,定位也快。

核心原则:层与层之间通过明确定义的接口通信。接口要轻量,最好是共享内存或者无锁队列。别用HTTP——那延迟你扛不住。

2.2 核心组件拆解

系统里最关键的四个组件,我一个个说。

2.2.1 行情组件

行情组件是系统的眼睛。它负责从交易所接收实时数据,然后分发给策略模块。

这里有个坑——行情数据是海量的。以国内期货为例,一个合约每秒可能产生几百笔行情。如果全量推送,带宽和CPU都扛不住。我在项目中遇到过,一开始没做行情过滤,结果策略模块处理不过来,直接丢数据。

解决方案是什么?行情预处理。在接入层就做一次清洗:去重、校验、聚合。只把有用的数据往上送。

// 伪代码:行情预处理
void onMarketData(MarketData md) {
    if (!validate(md)) return;  // 校验失败,丢弃
    if (isDuplicate(md)) return; // 重复数据,丢弃
    md = aggregate(md);          // 聚合tick数据
    publishToStrategy(md);       // 发布给策略
}

2.2.2 策略组件

策略组件是大脑。它拿到行情后,根据预设的规则或模型,决定要不要下单。

策略分两种:事件驱动型定时轮询型。高频交易里,几乎全是事件驱动。行情来了,立刻触发计算。轮询?那延迟你受不了。

我建议策略模块做成插件化。每个策略是一个独立的动态库,可以热加载。这样上线新策略不用重启系统。嗯,这个设计我在一个实盘项目里用过,效果不错。

避坑指南:策略模块里千万别做IO操作,比如写数据库、发网络请求。这些操作会阻塞计算线程。我曾经见过一个策略,因为里面加了个日志写入,延迟从5微秒飙到500微秒——直接废了。

2.2.3 交易组件

交易组件是手和脚。它负责把策略的决策变成实际的订单,发送到交易所。

这里有个关键点:订单生命周期管理。一个订单从发送到成交,中间可能经历多种状态:已发送、已确认、部分成交、全部成交、已撤销。交易组件必须准确跟踪每个订单的状态。

我习惯用一个订单状态机来管理。每个订单进来,状态机自动流转。如果出现异常状态(比如超时未确认),立刻告警并做处理。

订单状态 说明 下一步动作
PENDING 已生成,待发送 发送到交易所
SENT 已发送,等待确认 等待交易所回执
ACKED 交易所已确认 等待成交或撤销
FILLED 全部成交 通知策略,更新持仓
CANCELLED 已撤销 释放资源

2.2.4 风控组件

风控组件是刹车。没有它,系统可能一天就把一年的钱亏完。

风控要检查的东西很多:

  • 资金检查:账户余额够不够?
  • 持仓检查:会不会超仓?
  • 频率检查:每秒下单次数是否超限?
  • 价格检查:订单价格是否偏离市场太远?

风控必须放在交易组件之前。也就是说,策略的决策要先过风控,风控通过了,才能发到交易所。这个顺序不能乱。

注意:风控逻辑要尽可能简单、快速。别在风控里做复杂计算。我见过一个系统,风控里做了个机器学习模型预测市场——结果模型跑一次要10毫秒,订单早飞了。风控就是风控,别越界。

2.3 数据流与资金流

搞清楚了组件,咱们再看看数据怎么流、钱怎么流。

2.3.1 数据流

数据流是单向的,从行情到策略到交易。大致是这样的:

  1. 交易所推送行情数据到接入层。
  2. 接入层清洗、聚合后,推送给策略模块。
  3. 策略模块计算后,生成订单指令,发给风控。
  4. 风控检查通过后,订单指令发给交易模块。
  5. 交易模块发送订单到交易所,并接收回执。
  6. 回执信息再反向流回策略模块,用于更新状态。

你可能会问:为什么回执要流回策略?因为策略需要知道订单是否成交,才能决定下一步动作。比如一个限价单没成交,策略可能需要撤单重发。

这里有个设计要点:数据流要尽量少拷贝。每次拷贝都是延迟。我习惯用共享内存或者零拷贝技术,让数据在组件间直接传递。

2.3.2 资金流

资金流跟数据流不一样。资金流是事后清算的。也就是说,交易过程中,系统并不实时操作资金账户。它只是记录每一笔交易的盈亏,等到收盘后统一结算。

但注意,虽然不实时操作资金,但风控必须实时检查资金。比如账户里只有100万,策略却要下200万的单,风控必须拦住。

我建议在系统里维护一个虚拟资金账户。每次交易前,从虚拟账户里扣减保证金。成交后,更新盈亏。这样既不影响交易速度,又能实时控制风险。

一句话总结:数据流是实时的、单向的;资金流是事后的、清算的。两者不要混在一起处理。

2.4 架构设计的几个原则

最后,分享几个我这些年总结的原则:

  • 低耦合:组件之间只通过接口通信,别共享内存状态。
  • 高内聚:每个组件只做一件事,并且做好。
  • 可观测:每个组件都要暴露监控指标,出了问题能快速定位。
  • 可回放:行情数据要能回放,方便策略回测和问题排查。

嗯,这一章就到这里。下一章咱们深入行情组件,聊聊怎么在微秒级别处理海量数据。