量化交易系统全景

量化交易系统全景:从市场结构与参与者讲起,逐层拆解行情接入、策略研究、组合优化、订单执行、实时风控、清结算与合规,梳理低频到高频的延迟谱系、技术栈选型边界与团队分工,回答自研还是采购、Python 与 C++ 如何分工、延迟与成本怎样权衡等实战问题。

量化交易(Quantitative Trading)指用可编程的规则或模型替代人工判断,把「研究结论」编译成「可重复执行的订单流」。它的技术难点从来不在模型本身——一个三因子的线性模型在数学上十分钟就能写完——而在于让这套逻辑在真实市场里稳定、低延迟、可审计地跑上几年。真正吃掉项目预算的,是数据管道、执行成本、风控兜底和故障恢复,而不是那几行 alpha 公式。

真实证券与期货市场与游戏内经济、链上 DEX 有本质区别:这里有监管(程序化交易报备、异常交易监控)、有交易所侧的撮合与清算(大多数机构是接入方而不是做市方)、有 T+1 交割与保证金占用(资金不是即时到账)、还有确定的交易时段与涨跌停制度。这些约束直接决定系统形态:你不能像链上那样「原子性」地完成下单与结算,中间隔着交易所、券商、结算机构三层,每一层都可能拒单、延迟或失败。

本文按「从市场到系统」的顺序展开:先讲市场结构与策略谱系,再拆解系统分层、延迟谱系、数据基础设施,最后讨论技术栈选型、团队分工与自研采购的取舍。后续文章逐个下钻:行情接入、回测框架、因子研究、组合优化、订单管理、低延迟架构、撮合引擎、风控、清结算、实盘运维与合规。

目录

  1. 市场结构与参与者
  2. 策略谱系与频率分层
  3. 交易系统的七层架构
  4. 延迟谱系与硬件选型
  5. 数据基础设施
  6. 研究与生产的一致性
  7. 技术栈选型:Python 与 C++ 的边界
  8. 团队分工与研发流程
  9. 自研还是采购

1. 市场结构与参与者

理解系统之前先理解市场。国内常见的可程序化接入市场大致分四类,它们的协议、时段与结算规则差异极大:

市场典型品种接入协议交易时段结算
上期所/大商所/郑商所商品期货CTP日盘 + 夜盘T+0 逐日盯市
中金所股指、国债期货CTP09:30-15:00T+0 逐日盯市
沪深交易所股票、ETF券商柜台(极速/普通)09:30-15:00T+1 交割
银行间/场外债券、收益互换询价/API自定义双边

参与者分买方(公募、私募、自营)与卖方(券商、期货公司),技术栈差异很大:卖方更关注柜台稳定与合规,买方更关注策略研究与执行成本。做市商是特例——他们既是买方也是流动性提供方,对延迟的要求接近交易所,属于低延迟架构一文的重点读者。

三个容易被低估的市场规则:

  • 涨跌停:触板时盘口会出现大量虚假挂单,行情清洗必须识别,否则回测会「成交」在永远无法成交的价格上。
  • T+1 交割:当天买入的股票当天不能卖出,但资金 T+0 可用(可用不可取),持仓与可用量必须分开维护。
  • 集合竞价:09:15-09:25 的开盘集合竞价没有连续撮合,价格是单一清算价,报单逻辑与连续竞价完全不同。

1.1 交易时段与时钟

每个市场的交易时段都必须硬编码进系统,且要处理半日市、临时休市、夜盘跨日等特殊情况:

SESSIONS = [   # 简化的交易时段定义(A 股)
    ("09:15", "09:25", "OPEN_AUCTION"),   # 开盘集合竞价
    ("09:30", "11:30", "CONTINUOUS"),     # 上午连续竞价
    ("13:00", "14:57", "CONTINUOUS"),     # 下午连续竞价
    ("14:57", "15:00", "CLOSE_AUCTION"),  # 收盘集合竞价
]

def in_session(now):
    for start, end, kind in SESSIONS:
        if start <= now.strftime("%H:%M") <= end:
            return kind
    return None

非交易时段的报单会被交易所直接拒绝或排队到下一时段,系统必须在前置层拦截,避免无谓的通道占用。夜盘品种(如原油、黄金)跨自然日,日期归属要按「交易日」而不是「自然日」计算。

2. 策略谱系与频率分层

按持仓周期可以把策略分成五档,每档对系统的要求截然不同。这是决定整个技术栈选型的第一性问题:

频率持仓周期典型策略延迟要求主要成本
超高频 HFT毫秒~秒做市、套利微秒级延迟、手续费返还
高频分钟~小时统计套利、日内毫秒级冲击成本
中频天~周多因子选股秒级交易成本
低频周~月资产配置分钟级管理费
超低频季度以上宏观对冲小时级机会成本

频率越高,系统的工程量越大、容量越小。一个中频多因子策略每天可能只发几百笔单,用 Python + 券商 API 就能跑;而做市策略每秒要处理上万条行情、发出上千笔报单撤单,必须自己写 C++ 对接、内核旁路网络。选择策略频率时,先算清楚容量与成本,再决定投入多少工程资源。

容量估算的基本公式:

def estimate_capacity(daily_volume, participation_limit=0.05, holding_days=5):
    max_daily_trade = daily_volume * participation_limit  # 日均成交额 × 参与率
    capacity = max_daily_trade * holding_days  # 容量 = 单日可交易额 × 持仓周期
    return capacity

print(estimate_capacity(1e9, 0.05, 5))   # 日均成交 10 亿,参与率 5% → 约 25 亿

超过容量后,冲击成本会吞掉 alpha,这就是「策略越大越不赚钱」的量化解释。

3. 交易系统的七层架构

无论频率高低,一个完整的量化系统都可以拆成七层,自下而上:

┌─────────────────────────────────────────────┐
│ 7. 合规与审计层   报备、留痕、异常交易监控      │
├─────────────────────────────────────────────┤
│ 6. 运维监控层     指标、告警、灰度、故障切换     │
├─────────────────────────────────────────────┤
│ 5. 清结算层       成交回报、持仓、资金、对账     │
├─────────────────────────────────────────────┤
│ 4. 风控层         事前限额、事中拦截、事后复盘   │
├─────────────────────────────────────────────┤
│ 3. 执行层         OMS/EMS、拆单算法、报单撤单   │
├─────────────────────────────────────────────┤
│ 2. 策略层         信号生成、组合构建、目标仓位   │
├─────────────────────────────────────────────┤
│ 1. 数据层         行情接入、清洗、存储、回放     │
└─────────────────────────────────────────────┘

这个分层的价值在于故障隔离:数据层抖动不应该让风控误判,风控拦截不应该让执行层重发订单。每一层之间用明确定义的消息契约通信,具体如何用事件驱动方式组织,可以参考 分布式事件驱动架构 。

层与层之间的数据流是有方向的:行情从下往上流入策略,订单从上往下流出到交易所。反馈回路(成交回报、持仓变化)又从下往上回流,形成闭环。理解这个双向流是理解一切量化系统设计的基础。

3.1 层间消息契约

层与层之间的消息必须用显式的 schema 定义,而不是靠约定。以「策略到执行」这一层为例:

TargetPosition {
    symbol:      string      # 标的代码,如 600519.SH
    target_qty:  int64       # 目标持仓(股/手),负数表示做空
    urgency:     enum        # LOW / NORMAL / HIGH,影响拆单激进程度
    valid_until: int64       # 有效期(纳秒时间戳),过期作废
    strategy_id: string      # 策略标识,用于归因
}

用显式 schema 的收益:回测时可以替换一个「瞬时成交」的执行器,实盘时替换成「TWAP 拆单」的执行器,策略层代码完全不用改。这就是依赖倒置在交易系统里的具体体现。

4. 延迟谱系与硬件选型

延迟每降一个数量级,成本上升一个数量级。下表是各级别的大致量级(同城机房内):

层级端到端延迟典型实现成本
普通 API10~100 ms券商柜台 + 公网低
极速柜台1~10 ms券商极速柜台 + 专线中
直连交易所100 μs~1 ms托管机位 + 万兆高
内核旁路5~50 μsDPDK/Solarflare + 轮询很高
FPGA亚微秒硬件解析行情与报单极高

经验法则:只有当策略的边际收益对延迟敏感时,才为延迟买单。一个日均持仓一天的策略,把延迟从 10ms 降到 10μs 不会多赚一分钱,反而增加维护成本。判断标准是「信号衰减曲线」——如果信号在 100ms 后仍有 90% 的强度,就没必要做低延迟。

延迟预算的分配方式也值得说明。假设端到端目标 100μs,典型拆解:

行情网卡到用户态      15 μs   (内核旁路 + 轮询)
行情解码              5 μs    (预分配 buffer,零拷贝)
策略计算              10 μs   (无锁,无内存分配)
风控校验              5 μs    (规则预编译成位图)
报单编码 + 发送       20 μs   (协议模板预生成)
网络往返(交易所)    40 μs   (物理距离决定)
------------------------------------
合计                  95 μs

可以看到,物理距离和网络往返往往占大头,软件优化的空间是有上限的。这也是为什么托管机位(colocation)比软件优化更重要。

4.1 时间同步

延迟测量的前提是精确的时间。三个层次的方案:

方案精度成本适用
NTP1~10 ms免费中低频
PTP(IEEE 1588)亚微秒需硬件时间戳网卡高频
GPS/北斗授时纳秒需天线与恒温晶振交易所级

没有精确时钟,就无法区分「行情本身延迟」和「本地处理延迟」,延迟优化就变成盲人摸象。高频系统标配 PTP + 硬件时间戳网卡,每条报文的收发时间由网卡打戳,误差在百纳秒级。

5. 数据基础设施

数据层要解决四个问题:接入、清洗、存储、回放。

  • 接入:CTP 的 OnRtnDepthMarketData 是全量快照推送,沪深 L2 是增量订单簿,需要自己维护重建逻辑。
  • 清洗:剔除涨跌停虚假挂单、修复时间戳乱序、处理停牌与除权除息复权。
  • 存储:tick 数据量极大,单品种一年 L2 数据可达数十 GB,需要列式存储或时序库。
  • 回放:回测与实盘必须消费同一份数据契约,否则回测结果不可信。

存储选型上,冷热分层是标准做法,历史 tick 归档到对象存储的实践可以参考 数据归档与分层 。热数据(近 3 个月)放 SSD 或内存,温数据放本地磁盘,冷数据(一年以上)压缩后归档。

数据量估算:

单品种 L2 快照:约 3 笔/秒(A 股)→ 每天 4 小时 = 43,200 条
单条快照约 200 字节 → 单品种单日约 8.6 MB
全市场 5000 只股票 → 单日约 43 GB
一年 240 个交易日 → 约 10 TB(未压缩)
Parquet + ZSTD 压缩比约 5:1 → 约 2 TB

这个量级决定了你不能用 MySQL 存 tick,必须用列式格式或专门的时序数据库。

5.1 数据质量监控

数据管道必须有质量检查,否则脏数据会静默污染所有下游策略。常见的检查项:

完整性:每日 tick 数量是否落在历史同期的 ±30% 区间
连续性:相邻快照的时间差是否超过阈值(如 3 秒无更新)
合理性:价格是否在涨跌停区间内,成交量是否非负
一致性:同一时刻的多源报价偏差是否超阈值

任何一项失败都应该告警并阻断当日的自动交易,宁可停一天也不能用脏数据下单。

6. 研究与生产的一致性

这是量化系统最经典的工程问题:研究员用 Python + pandas 在 notebook 里跑出年化 30% 的回测,上线后实盘亏损。根因通常是研究环境与生产环境的数据、时间、撮合逻辑不一致。

治理方法有三条:

  1. 统一数据源:研究与生产读同一份清洗后的数据集,禁止研究员自己 read_csv 临时数据。
  2. 统一时间语义:明确每个字段是「事件时间」还是「接收时间」,回测时严禁用未来数据。
  3. 统一撮合模型:回测的成交假设(按收盘价成交、无滑点)必须与实盘执行的统计特征对齐。

一个可落地的做法是把清洗逻辑封装成库,研究回测实盘三方共用:

class Bar:   # 研究、回测、实盘三方共用的数据契约
    ts: int          # 事件时间(交易所时间戳,纳秒)
    symbol: str
    open: float
    high: float
    low: float
    close: float
    volume: int

def load_bars(symbol: str, start: int, end: int) -> list[Bar]:
    ...   # 回测读本地 Parquet,实盘读实时流,接口完全一致

只要接口一致,研究员写的 on_bar 逻辑就能一行不改地跑在实盘上。

7. 技术栈选型:Python 与 C++ 的边界

主流实践是「Python 做研究、C++/Rust 做执行」,中间用标准化的信号文件或消息队列衔接:

import pandas as pd   # 研究侧:产出目标仓位,写入标准化文件

target = compute_alpha_signals(features)      # 因子到信号
weights = optimize_portfolio(target, cov)     # 组合优化
weights.to_parquet(f"targets/{date}.parquet") # 生产侧消费
// 生产侧:C++ 读目标仓位,做执行与风控
TargetBook load_targets(const std::string& path);
void on_tick(const Tick& t) {
    auto orders = exec_algo_.generate(t, book_);   // TWAP/POV 拆单
    for (auto& o : orders) risk_.check_then_send(o);
}

边界划在哪里?经验规则:延迟敏感的路径用 C++,逻辑复杂的路径用 Python。因子计算、组合优化、回测可以全 Python;行情解析、撮合对接、风控校验必须 C++。Go 适合中间层(网关、监控、清结算服务),因为它的并发模型简单、部署方便,GC 停顿在毫秒级对中低频策略完全够用。

Rust 近年在执行层也开始流行,优势是内存安全 + 无 GC,代价是生态不如 C++ 成熟,特别是交易所 API 的 SDK 大多只提供 C++/C 版本。

三种语言的对比:

语言延迟开发效率生态推荐场景
Python高(ms 级)极高极好(numpy/pandas)研究、回测、低频
C++极低(μs 级)低好(交易所 SDK)高频、执行、风控
Rust极低中一般新一代执行层
Go低(亚 ms)高好网关、监控、清结算

跨语言通信的成本要提前算进去:进程间用共享内存或 mmap 文件可以做到微秒级,用 HTTP/JSON 则是毫秒级,差三个数量级。高频系统的策略与执行通常在同一进程内,通过函数调用而非 IPC 衔接。

8. 团队分工与研发流程

一个 10 人左右的量化团队典型分工:

角色人数主要产出
策略研究员4因子、模型、回测报告
交易系统工程师2OMS/EMS、低延迟链路
数据工程师2行情管道、数据仓库
运维/风控1监控、限额、值班
合规1报备、留痕、审计

研发流程上,策略上线要过四道关:回测(含样本外)→ 仿真盘(paper trading)→ 小资金实盘 → 逐步加仓。跳过仿真盘直接实盘是新手最常见的错误,因为回测无法暴露柜台拒单、部分成交、行情丢包这些真实问题。

每一道关卡的通过标准应该量化:

回测:     年化 > 15%,最大回撤 < 10%,夏普 > 1.5(样本外)
仿真盘:   与回测的日收益相关性 > 0.8,运行 20 个交易日无异常
小资金:   实盘滑点与仿真偏差 < 20%,运行 1 个月
加仓:     每次加仓不超过当前规模的 50%,观察 2 周

9. 自研还是采购

不是所有组件都值得自研。经验判断:

  • 必须自研:策略逻辑、信号生成、执行算法(这是核心竞争力)。
  • 可以采购:行情源、券商柜台、回测平台(如 Backtrader/vn.py)、风控系统。
  • 建议开源:监控(Prometheus + Grafana)、消息队列(Kafka)、存储(ClickHouse/Parquet)。

采购的隐藏成本是适配成本——一套商用风控系统接入你的 OMS 可能需要几个月,且无法定制特殊规则。自研的隐藏成本是维护成本——行情协议一旦升级,你的解析代码就得跟着改。中小团队的现实选择是:核心自研,外围尽量用成熟方案。

一个判断自研价值的简单框架:

问题是否
是否构成竞争优势?自研采购
是否有成熟开源方案?开源自研/采购
团队是否有维护能力?自研采购
需求是否高度定制?自研采购

权衡取舍

维度自研采购/开源
延迟可做到极致受制于对方实现
成本人力投入高一次性/订阅费
灵活性完全可控受 API 限制
上线速度慢(月级)快(周级)
维护负担长期由供应商承担

频率选择上的取舍同样清晰:低频策略用重型低延迟基础设施是浪费,高频策略用 Python 是自缚手脚。先定策略频率,再定架构档次,不要反过来。

语言选型上也有类似的取舍:Python 开发效率高但延迟不可控,C++ 延迟可控但开发慢、易出内存错误。混合架构的代价是引入了跨语言通信的复杂度(序列化、进程管理、版本同步),这部分开销常常被低估。

常见坑清单

  1. 回测赚钱实盘亏钱:多因前视偏差或未计交易成本,回测必须加入滑点与手续费模型。
  2. 行情丢包导致持仓错乱:增量行情未做序号校验,重启后状态不一致,需定期与柜台对账。
  3. 订单状态机不完整:只处理「已成交」,忽略「部分成交/已撤/废单」,导致持仓统计错误。
  4. 风控只做事后:限额在报单前校验才有意义,事后统计只能用于复盘。
  5. 单点故障无备份:行情、报单通道都要有主备,且要能自动切换。
  6. 时间不同步:多机部署未做 NTP/PTP 对时,成交回报时序错乱,无法归因。
  7. 资金与持仓缓存不准:必须以柜台回报为准,本地缓存只是加速,不可作为事实来源。
  8. 上线跳过仿真盘:仿真盘能暴露柜台拒单、部分成交等回测覆盖不到的问题。
  9. 监控只看盈亏:还要监控报单延迟、撤单率、行情延迟、通道连接状态。
  10. 合规留痕缺失:监管要求全链路可回溯,日志至少要保留数年。

小结

量化交易系统的本质是把研究结论可靠地变成订单,并把风险控制在可承受范围内。技术难点的分布极不均匀:低频策略 80% 的功夫在数据与回测,高频策略 80% 的功夫在延迟与稳定性。先想清楚自己的策略落在谱系的哪一档,再决定投入。

全景观的另一层含义是边界感:知道哪些问题属于数据层、哪些属于执行层、哪些属于风控层,遇到故障时才能快速定位。一个持仓对不上的 bug,可能根因在行情丢包、也可能在订单状态机、也可能在清结算的记账逻辑——分层清晰才有一一排查的路径。

后续文章会逐个下钻:从 行情数据接入与清洗 开始打地基,再进入 回测框架设计与前视偏差 ,然后是因子、组合、执行、低延迟、撮合、风控、清结算、运维与合规。建议按顺序读,也可以直接跳到与你当前痛点最相关的一篇。如果需要先补齐分布式与可观测性的基础,可以先看分布式链路追踪相关内容。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「量化交易」更多文章

  1. 风险模型与因子归因
  2. 回测偏差与过拟合防范
  3. 市场微结构与流动性