第二章:回测框架设计原则
做高频交易回测,框架设计是地基。地基没打好,后面全是坑。我这些年踩过的坑,十个有八个跟框架设计有关。今天咱们聊聊三个核心原则:模块化、事件驱动、性能优化。
一、模块化设计:别把所有鸡蛋放一个篮子里
模块化,说白了就是「各司其职」。你的回测框架里,数据加载、策略逻辑、订单管理、风控检查……这些功能应该各自独立。为什么?
我在项目中遇到过一件事:有个同事把数据清洗和策略逻辑写在一个函数里。结果换了个数据源,整个策略逻辑都得重写。嗯,那场面,挺尴尬的。
核心原则:每个模块只做一件事,并且做好一件事。
我建议这样拆分:
- 数据模块:负责行情数据的加载、清洗、对齐。输出标准化的 OHLCV 格式。
- 策略模块:接收行情数据,输出交易信号。不关心订单怎么执行。
- 风控模块:检查信号是否合规。比如仓位限制、最大回撤。
- 执行模块:把信号变成订单,模拟撮合。
- 记录模块:记录每一笔交易、每个持仓变化。
你想想看,如果哪天你要换数据源,只需要改数据模块。策略逻辑完全不用动。这就是模块化的好处。
小技巧:用接口(Interface)来定义模块之间的通信协议。这样每个模块可以独立开发、独立测试。
二、事件驱动架构:让系统自己动起来
高频交易里,行情是源源不断的。你不能写个死循环去轮询。那样 CPU 会炸,延迟也会高。
事件驱动架构,就是让系统「被动响应」。行情来了,触发一个事件;订单成交了,触发另一个事件。每个事件都有对应的处理器。
我习惯这样设计:
class Event:
def __init__(self, event_type, data):
self.type = event_type
self.data = data
self.timestamp = time.time()
class EventEngine:
def __init__(self):
self.handlers = {}
def register(self, event_type, handler):
if event_type not in self.handlers:
self.handlers[event_type] = []
self.handlers[event_type].append(handler)
def put(self, event):
handlers = self.handlers.get(event.type, [])
for handler in handlers:
handler(event)
你看,代码很简单。但核心思想很重要:事件是中心,模块是订阅者。
举个例子:
- 行情模块收到 tick 数据 → 发布
TICK_EVENT - 策略模块订阅
TICK_EVENT→ 计算信号 → 发布SIGNAL_EVENT - 风控模块订阅
SIGNAL_EVENT→ 检查 → 发布ORDER_EVENT - 执行模块订阅
ORDER_EVENT→ 模拟撮合 → 发布FILL_EVENT
每个模块只关心自己订阅的事件。模块之间完全解耦。我曾经把一个策略从「均线策略」换成「机器学习策略」,只改了策略模块。其他模块一行代码没动。
注意:事件队列要小心处理。如果事件处理太慢,队列会积压。高频场景下,建议用无锁队列或者环形缓冲区。
三、性能优化要点:每一微秒都很重要
高频回测,数据量巨大。一天的数据可能几百万条 tick。如果代码写得糙,回测跑一天都出不来结果。那还怎么调参数?
我总结几个关键点:
1. 数据结构选型
别用 Python 的 list 存 tick 数据。用 numpy array 或者 pandas DataFrame。为什么?因为底层是 C 实现的,循环快得多。
# 慢
prices = []
for tick in ticks:
prices.append(tick.price)
# 快
import numpy as np
prices = np.array([tick.price for tick in ticks])
2. 避免对象创建
高频场景下,每毫秒都在创建对象。GC(垃圾回收)会频繁触发,导致卡顿。我建议:
- 用对象池复用对象
- 用
__slots__减少内存开销 - 避免在热路径上创建临时变量
3. 向量化计算
能用矩阵运算,就别用 for 循环。比如计算移动平均线:
# 慢
ma = []
for i in range(20, len(prices)):
ma.append(np.mean(prices[i-20:i]))
# 快
import pandas as pd
ma = pd.Series(prices).rolling(20).mean().values
4. 预分配内存
如果你知道数据量,提前分配好内存。别让 Python 动态扩容。
性能对比(实测数据):
| 优化项 | 优化前 | 优化后 | 提升倍数 |
|---|---|---|---|
| 数据结构 | Python list | numpy array | 10x |
| 循环方式 | for 循环 | 向量化 | 50x |
| 对象创建 | 每次 new | 对象池 | 5x |
四、框架整体架构图
下面这张图,是我个人习惯的框架结构。你可以参考,但别照搬。每个团队、每个策略都有自己的特点。
五、避坑指南
最后,分享几个我踩过的坑:
- 我曾经把日志打印放在热路径上。结果回测速度慢了 30 倍。后来改成异步日志,问题解决。
- 我曾经用 Python 的
datetime做时间戳比较。高频场景下,这个操作很慢。换成int64纳秒时间戳,速度快了一个数量级。 - 我曾经在事件处理里做数据库写入。结果事件队列越积越多。后来改成批量写入,每 1000 个事件写一次。
我的建议:先写出能跑的版本,再优化。别一开始就追求极致性能。80% 的性能提升来自 20% 的代码优化。
好了,模块化、事件驱动、性能优化,这三个原则你记住了吗?下次写回测框架时,试着用这些思路去设计。你会发现,代码好维护了,跑得也快了。
公众号:蓝海资料掘金营,微信deep3321