1. 回测系统测试方法论:单元测试、集成测试、端到端测试在回测中的角色与分工

做回测系统这些年,我踩过最大的坑是什么?不是策略逻辑写错了,而是测试没做到位。你想想看,一个回测系统里跑着几百万行代码,从数据清洗到订单路由,从撮合引擎到资金核算,任何一个环节出问题,你得到的回测结果都是废纸一张。

所以今天咱们聊聊测试方法论。说白了,就是怎么用单元测试、集成测试、端到端测试这三板斧,把回测系统的质量守住。

1.1 单元测试:守住最小逻辑单元

单元测试,测的是单个函数或模块。比如一个计算夏普比率的函数,一个判断是否触发止损的逻辑,一个K线合并的算法。这些是回测系统最基础的砖块。

我个人习惯,每个核心函数至少写3个测试用例:正常输入、边界值、异常输入。举个例子:

# 测试夏普比率计算函数
def test_sharpe_ratio_normal():
    returns = [0.01, 0.02, -0.01, 0.03, 0.005]
    result = calculate_sharpe(returns, risk_free=0.02)
    assert abs(result - 1.23) < 0.01  # 预期值

def test_sharpe_ratio_empty():
    returns = []
    result = calculate_sharpe(returns, risk_free=0.02)
    assert result == 0.0  # 空序列返回0

def test_sharpe_ratio_constant():
    returns = [0.01, 0.01, 0.01]
    result = calculate_sharpe(returns, risk_free=0.02)
    assert result == float('inf')  # 无波动时夏普无穷大
我的经验:单元测试跑得越快越好。我要求团队里每个单元测试的执行时间不超过50毫秒。超过这个数,说明被测函数太复杂,该拆分了。

为什么要这么较真?我在项目中遇到过一件事:一个看起来简单的calculate_position_size函数,因为浮点数精度问题,在极端行情下算出了负数仓位。单元测试一跑,立马现原形。要是等到集成测试才发现,定位问题得花半天。

1.2 集成测试:验证模块间的握手

单元测试过了,不代表系统能跑通。集成测试要验证的是模块之间的交互。比如数据模块给策略模块喂数据,策略模块给风控模块发信号,风控模块给订单模块下指令。这些链路一旦断了,回测结果就是错的。

我一般把集成测试分成两类:

  • 数据流集成测试:检查数据从原始CSV到内存DataFrame的转换是否正确,时间戳对齐有没有丢数据,复权计算是否准确。
  • 信号流集成测试:模拟一个完整的交易信号从生成到执行的路径,看最终成交记录是否合理。

举个例子,我曾经遇到一个经典问题:数据模块返回的OHLCV数据,时间戳是UTC的,但策略模块默认用了本地时间。结果回测时,每天的开盘价都错位了一个小时。集成测试里加一个「数据时间戳一致性检查」,这种问题一抓一个准。

核心原则:集成测试不追求全覆盖,而是覆盖关键路径。回测系统里最关键的路径就是「数据→策略→风控→订单→成交→资金」。这条路径上的每个接口,都要有集成测试。

1.3 端到端测试:模拟真实交易场景

端到端测试,说白了就是拿一个完整的策略,跑一遍完整的回测流程,然后跟预期结果对比。这是最接近真实交易的测试方式。

我建议端到端测试用已知结果的场景。比如:

  • 一个简单的均线金叉策略,在2018年BTC的行情下,预期收益是多少?
  • 一个网格交易策略,在震荡行情下,预期成交次数和盈亏是多少?
  • 一个套利策略,在价差固定的情况下,预期无风险收益是多少?

这些场景的结果,你可以用手算或者Excel验证。然后让回测系统跑一遍,看结果是否一致。

注意:端到端测试最怕「假阳性」。就是测试通过了,但实际结果是错的。为什么会这样?因为测试数据太干净了。真实行情有跳空、有停牌、有数据缺失。所以端到端测试一定要用真实的历史数据,别用人工合成的完美数据。

我记得有一次,团队用模拟数据做端到端测试,所有指标都完美。结果一上真实数据,回测结果差了30%。查了半天,发现是数据清洗模块把停牌日的价格填充成了前收盘价,导致策略在停牌日「偷偷」交易了。端到端测试用真实数据,这种问题才能暴露。

1.4 三种测试的分工与配合

这三种测试不是互相替代的关系,而是互补的。我画了一张图来说明它们的分工:

回测系统测试金字塔 单元测试 数量最多 · 执行最快 · 覆盖最细 集成测试 数量适中 · 验证接口 · 发现交互问题 端到端测试 数量最少 · 模拟真实 · 验证整体 测试数量 测试粒度 越往上,测试越接近真实交易,但执行越慢、定位问题越难

从这张图你能看出什么?

  • 单元测试是地基,数量最多,跑得最快。它保证每个函数都是对的。
  • 集成测试是承重墙,数量适中。它保证模块之间能正常通信。
  • 端到端测试是屋顶,数量最少。它保证整个系统能完成一次完整的回测任务。

我建议的配比是:70%单元测试,20%集成测试,10%端到端测试。这个比例不是拍脑袋定的,而是基于我多年的经验。单元测试写得多,后期改代码时心里有底。集成测试写太多,维护成本会爆炸。端到端测试写太多,跑一次要等半天,反而拖慢开发节奏。

1.5 避坑指南:我踩过的三个坑

最后分享几个我亲身踩过的坑,希望能帮你少走弯路。

坑一:测试数据太干净
我曾经用人工合成的完美K线数据做测试,所有测试都通过了。结果一上真实数据,回测结果跟预期差了20%。原因是真实数据里有跳空、有复权、有停牌,这些在人工数据里都没模拟到。后来我强制要求:所有测试数据必须来自真实历史行情,最多做一下裁剪。
坑二:忽略浮点数精度
回测系统里大量涉及金额计算。浮点数精度问题会导致资金核算差几分钱。别小看这几分钱,在千次交易后,误差会累积到几百块。我的解决方案是:所有金额计算用Decimal类型,或者用整数表示最小单位(比如用「分」代替「元」)。
坑三:测试用例不维护
很多团队写完测试用例就不管了。等代码重构后,测试用例还是老的,跑出来全是红的。我要求团队:每次修改核心逻辑,必须同步更新相关测试用例。测试用例跟代码一样,需要持续维护。

嗯,测试方法论就聊到这里。记住一句话:没有经过充分测试的回测系统,跑出来的结果只是数字,不是结论。下一节咱们聊聊具体怎么搭建测试框架,包括pytest的配置、mock的使用、以及如何做性能测试。到时候我会分享一些实战中的小技巧。

一句话总结:单元测试保正确,集成测试保连通,端到端测试保真实。三者缺一不可。

专注资料整理