量化交易(Quantitative Trading)指用可编程的规则或模型替代人工判断,把「研究结论」编译成「可重复执行的订单流」。它的技术难点从来不在模型本身——一个三因子的线性模型在数学上十分钟就能写完——而在于让这套逻辑在真实市场里稳定、低延迟、可审计地跑上几年。真正吃掉项目预算的,是数据管道、执行成本、风控兜底和故障恢复,而不是那几行 alpha 公式。
真实证券与期货市场与游戏内经济、链上 DEX 有本质区别:这里有监管(程序化交易报备、异常交易监控)、有交易所侧的撮合与清算(大多数机构是接入方而不是做市方)、有 T+1 交割与保证金占用(资金不是即时到账)、还有确定的交易时段与涨跌停制度。这些约束直接决定系统形态:你不能像链上那样「原子性」地完成下单与结算,中间隔着交易所、券商、结算机构三层,每一层都可能拒单、延迟或失败。
本文按「从市场到系统」的顺序展开:先讲市场结构与策略谱系,再拆解系统分层、延迟谱系、数据基础设施,最后讨论技术栈选型、团队分工与自研采购的取舍。后续文章逐个下钻:行情接入、回测框架、因子研究、组合优化、订单管理、低延迟架构、撮合引擎、风控、清结算、实盘运维与合规。
目录
- 市场结构与参与者
- 策略谱系与频率分层
- 交易系统的七层架构
- 延迟谱系与硬件选型
- 数据基础设施
- 研究与生产的一致性
- 技术栈选型:Python 与 C++ 的边界
- 团队分工与研发流程
- 自研还是采购
1. 市场结构与参与者
理解系统之前先理解市场。国内常见的可程序化接入市场大致分四类,它们的协议、时段与结算规则差异极大:
| 市场 | 典型品种 | 接入协议 | 交易时段 | 结算 |
|---|---|---|---|---|
| 上期所/大商所/郑商所 | 商品期货 | CTP | 日盘 + 夜盘 | T+0 逐日盯市 |
| 中金所 | 股指、国债期货 | CTP | 09:30-15:00 | T+0 逐日盯市 |
| 沪深交易所 | 股票、ETF | 券商柜台(极速/普通) | 09:30-15:00 | T+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. 延迟谱系与硬件选型
延迟每降一个数量级,成本上升一个数量级。下表是各级别的大致量级(同城机房内):
| 层级 | 端到端延迟 | 典型实现 | 成本 |
|---|---|---|---|
| 普通 API | 10~100 ms | 券商柜台 + 公网 | 低 |
| 极速柜台 | 1~10 ms | 券商极速柜台 + 专线 | 中 |
| 直连交易所 | 100 μs~1 ms | 托管机位 + 万兆 | 高 |
| 内核旁路 | 5~50 μs | DPDK/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 时间同步
延迟测量的前提是精确的时间。三个层次的方案:
| 方案 | 精度 | 成本 | 适用 |
|---|---|---|---|
| NTP | 1~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% 的回测,上线后实盘亏损。根因通常是研究环境与生产环境的数据、时间、撮合逻辑不一致。
治理方法有三条:
- 统一数据源:研究与生产读同一份清洗后的数据集,禁止研究员自己
read_csv临时数据。 - 统一时间语义:明确每个字段是「事件时间」还是「接收时间」,回测时严禁用未来数据。
- 统一撮合模型:回测的成交假设(按收盘价成交、无滑点)必须与实盘执行的统计特征对齐。
一个可落地的做法是把清洗逻辑封装成库,研究回测实盘三方共用:
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 | 因子、模型、回测报告 |
| 交易系统工程师 | 2 | OMS/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++ 延迟可控但开发慢、易出内存错误。混合架构的代价是引入了跨语言通信的复杂度(序列化、进程管理、版本同步),这部分开销常常被低估。
常见坑清单
- 回测赚钱实盘亏钱:多因前视偏差或未计交易成本,回测必须加入滑点与手续费模型。
- 行情丢包导致持仓错乱:增量行情未做序号校验,重启后状态不一致,需定期与柜台对账。
- 订单状态机不完整:只处理「已成交」,忽略「部分成交/已撤/废单」,导致持仓统计错误。
- 风控只做事后:限额在报单前校验才有意义,事后统计只能用于复盘。
- 单点故障无备份:行情、报单通道都要有主备,且要能自动切换。
- 时间不同步:多机部署未做 NTP/PTP 对时,成交回报时序错乱,无法归因。
- 资金与持仓缓存不准:必须以柜台回报为准,本地缓存只是加速,不可作为事实来源。
- 上线跳过仿真盘:仿真盘能暴露柜台拒单、部分成交等回测覆盖不到的问题。
- 监控只看盈亏:还要监控报单延迟、撤单率、行情延迟、通道连接状态。
- 合规留痕缺失:监管要求全链路可回溯,日志至少要保留数年。
小结
量化交易系统的本质是把研究结论可靠地变成订单,并把风险控制在可承受范围内。技术难点的分布极不均匀:低频策略 80% 的功夫在数据与回测,高频策略 80% 的功夫在延迟与稳定性。先想清楚自己的策略落在谱系的哪一档,再决定投入。
全景观的另一层含义是边界感:知道哪些问题属于数据层、哪些属于执行层、哪些属于风控层,遇到故障时才能快速定位。一个持仓对不上的 bug,可能根因在行情丢包、也可能在订单状态机、也可能在清结算的记账逻辑——分层清晰才有一一排查的路径。
后续文章会逐个下钻:从 行情数据接入与清洗 开始打地基,再进入 回测框架设计与前视偏差 ,然后是因子、组合、执行、低延迟、撮合、风控、清结算、运维与合规。建议按顺序读,也可以直接跳到与你当前痛点最相关的一篇。如果需要先补齐分布式与可观测性的基础,可以先看分布式链路追踪相关内容。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。