第二十讲:回测系统设计
回测系统,说白了就是量化策略的「时光机」。你写了个策略,能不能赚钱?回测告诉你。但这里有个坑——回测漂亮不代表实盘能打。我见过太多团队,回测曲线美如画,一上实盘就崩盘。今天我们就聊聊怎么设计一个靠谱的回测系统。
一、事件驱动回测:让策略「活」起来
我个人习惯把回测引擎分成两类:向量化回测和事件驱动回测。向量化回测快,但不够真实。事件驱动回测慢,但更贴近实盘。
什么叫事件驱动?就是模拟真实市场的消息流。比如:
- 收到一条成交回报
- 收到一条行情Tick
- 收到一条订单状态变更
每个事件触发一个回调函数。你的策略就挂在这些回调上。
核心架构:
class EventEngine:
def __init__(self):
self.queue = Queue()
self.handlers = {}
def register(self, event_type, handler):
self.handlers[event_type] = handler
def run(self):
while True:
event = self.queue.get()
handler = self.handlers.get(event.type)
if handler:
handler(event)
嗯,这里要注意:事件队列不能阻塞。我曾经在一个项目中,因为事件处理太慢,导致回测结果严重失真。后来加了异步处理,才解决问题。
二、多资产回测:别只盯着一个品种
做市商系统很少只做一个品种。你想想看,一个结构化产品可能挂钩5个标的,每个标的又有多个期限。这时候单资产回测就不够用了。
多资产回测的核心难点在于:
- 时间对齐:不同资产交易时间不同,怎么对齐?
- 资金分配:总资金怎么分配到各个资产上?
- 相关性处理:资产之间的相关性怎么模拟?
我建议的做法是:
- 统一时间戳精度到毫秒级
- 用「资金管理器」统一管理头寸
- 引入协方差矩阵做联合模拟
避坑指南:我曾经遇到过一个坑——不同交易所的节假日不同。比如A股休市,港股还在交易。这时候你的多资产回测必须考虑「部分资产停牌」的情况。否则回测结果会虚高。
三、市场冲击模型:大单的隐形杀手
很多新手做回测,假设「想买就能买,想卖就能卖」。这是最大的幻觉。真实市场中,你的大单会推动价格变化。这就是市场冲击。
常用的冲击模型有几种:
| 模型名称 | 核心公式 | 适用场景 |
|---|---|---|
| 线性冲击模型 | ΔP = λ × Q | 流动性较好的市场 |
| 平方根冲击模型 | ΔP = λ × √Q | 流动性一般的市场 |
| Almgren-Chriss模型 | ΔP = α × Q + β × σ × √(Q/V) | 最接近真实市场 |
我个人比较推荐Almgren-Chriss模型。它同时考虑了永久冲击和临时冲击。我在做期权做市商系统时,用这个模型校准过冲击参数,回测结果和实盘偏差控制在5%以内。
注意:冲击模型的参数需要定期校准。市场环境变了,参数也要跟着变。我见过有人一套参数用三年,结果回测越来越不准。
四、回测过拟合防范:别被漂亮曲线骗了
这是回测系统里最容易被忽视的问题。你调参数、改策略,回测曲线越来越漂亮。但这是真的吗?
过拟合的典型症状:
- 回测夏普比率超过3
- 策略参数极其敏感
- 样本外测试一塌糊涂
怎么防范?我分享几个实战经验:
- 交叉验证:把数据分成训练集、验证集、测试集。训练集调参,验证集选模型,测试集做最终评估。
- 参数稳定性测试:稍微改变参数,看策略表现是否剧烈波动。如果是,说明过拟合了。
- 蒙特卡洛模拟:对回测结果做1000次随机扰动,看收益分布是否稳定。
一个实用的过拟合检测方法:
def deflated_sharpe_ratio(sharpe, T, N):
"""
计算缩水夏普比率
sharpe: 回测夏普
T: 样本数量
N: 尝试的策略数量
"""
import scipy.stats as stats
E_max = stats.norm.ppf(1 - 1/N)
V_max = stats.norm.ppf(1 - 1/N * np.exp(-1)) - E_max
return (sharpe - E_max) / np.sqrt(V_max / T)
这个公式可以告诉你:在尝试了N个策略后,你的回测夏普有多少是「运气成分」。如果缩水后的夏普低于1,那基本可以判定过拟合了。
五、回测系统的整体架构
说了这么多,我们来看看回测系统的整体架构。下面这张图是我在实际项目中总结出来的:
这张图展示了回测系统的四个层次。数据层负责数据清洗和对齐,引擎层负责事件调度和模拟,策略层跑你的交易逻辑,输出层生成报告。每一层都可以独立替换,方便做单元测试。
我的经验:回测系统一定要做「回放模式」。就是逐笔回放历史数据,看策略在每个时间点的决策。这比看最终曲线有用得多。我曾经靠这个发现了一个隐藏的bug——策略在收盘前最后一秒还在下单,而实盘根本来不及成交。
好了,回测系统设计就聊到这里。记住一句话:回测是用来发现问题的,不是用来证明策略牛逼的。保持怀疑,多问几个「为什么」,你的回测系统才能真正帮到你。