一、回测系统的核心模块

做量化投资,回测系统就是你的实验室。我刚开始做回测时,以为就是拿历史数据跑一遍策略。后来踩了不少坑,才明白一个完整的回测系统,至少需要这几个核心模块。

1.1 数据管理模块

数据是回测的原材料。这个模块负责三件事:

  • 数据获取:从交易所、数据商那里拉取行情数据
  • 数据清洗:处理缺失值、异常值、复权调整
  • 数据存储:用HDF5、Parquet或数据库存起来,方便快速读取

我个人习惯把日线数据存成Parquet格式,读取速度比CSV快10倍不止。嗯,这里要注意:数据对齐是个大坑,不同股票的交易日期可能不一样,必须统一处理。

1.2 策略逻辑模块

这就是你的交易想法。比如「当5日均线上穿20日均线时买入」。这个模块要足够灵活,方便你快速迭代策略。

我一般把策略拆成三个部分:

  • 信号生成:根据因子或指标产生买卖信号
  • 仓位管理:每次交易多少仓位,要不要加杠杆
  • 风控规则:止损、止盈、最大回撤限制

1.3 交易执行模块

信号出来了,怎么模拟成交?这里要考虑:

  • 滑点模型:实际成交价和信号价之间的差距
  • 手续费:佣金、印花税、过户费
  • 流动性限制:大单能不能成交?会不会把价格打飞?

我曾经在回测里忽略滑点,结果实盘时收益直接腰斩。说白了,回测里赚的钱,有一半是理想情况下的幻觉。

1.4 绩效分析模块

跑完回测,你得知道策略到底行不行。这个模块输出:

  • 年化收益率、夏普比率、最大回撤
  • 胜率、盈亏比、交易次数
  • 收益曲线、回撤曲线、月度收益热力图

核心要点:这四个模块缺一不可。数据是基础,策略是灵魂,执行是现实,分析是镜子。

二、数据流设计

数据在回测系统里怎么流动?我画了一张图,你看完就明白了。

数据源 交易所/数据商 数据清洗 去重/对齐/复权 数据存储 Parquet/HDF5 策略引擎 信号生成 + 仓位管理 交易模拟 滑点/手续费/流动性 绩效分析 夏普/回撤/胜率 回测系统数据流图

数据从左边进来,经过清洗、存储,然后被策略引擎调用。策略产生信号后,交给交易模拟模块执行,最后输出绩效分析结果。整个流程是单向的,但你可以随时回到策略模块调整参数。

我的经验:数据流设计时,一定要考虑增量更新。每天只拉取新增数据,而不是全量重跑。我见过有人每天全量下载A股数据,结果硬盘很快就爆了。

三、事件驱动架构 vs 向量化回测

这是回测系统设计里最核心的抉择。两种方式各有千秋,我分别说说。

3.1 事件驱动架构

说白了,就是模拟真实交易环境。系统按时间顺序处理一个个事件:

  • 行情数据到达 → 触发信号计算
  • 信号产生 → 触发订单生成
  • 订单提交 → 触发成交回报

代码大概长这样:

class EventDrivenBacktest:
    def __init__(self):
        self.events = []
        self.portfolio = Portfolio()
        self.strategy = Strategy()
    
    def run(self, data):
        for bar in data:
            # 处理市场数据事件
            event = MarketEvent(bar)
            self.events.append(event)
            
            # 策略生成信号
            signal = self.strategy.on_bar(bar)
            if signal:
                order_event = OrderEvent(signal)
                self.events.append(order_event)
            
            # 执行订单
            fill_event = self.executor.execute(order_event)
            self.portfolio.update(fill_event)

优点很明显:

  • 更贴近真实交易,能处理复杂逻辑
  • 支持异步操作,比如同时监控多个品种
  • 容易扩展,加个风控模块很方便

缺点也有:

  • 速度慢,尤其是回测几千只股票时
  • 代码复杂度高,调试起来头疼

3.2 向量化回测

这种方式就暴力多了。一次性把整个历史数据算完,用矩阵运算代替循环。

import pandas as pd
import numpy as np

def vectorized_backtest(price_data, signals):
    # 一次性计算所有信号
    positions = signals.shift(1)  # 第二天才执行
    
    # 计算每日收益率
    returns = price_data.pct_change()
    strategy_returns = positions * returns
    
    # 累计收益
    cumulative = (1 + strategy_returns).cumprod()
    return cumulative

你想想看,用向量化方式回测沪深300所有成分股,可能几秒钟就跑完了。而事件驱动可能要跑几分钟。

但向量化也有硬伤:

  • 无法处理条件逻辑,比如「如果今天涨停,明天不买」
  • 不能模拟订单簿、滑点等微观结构
  • 对高频策略基本不适用

3.3 怎么选?

对比维度 事件驱动 向量化
执行速度
代码复杂度
真实度
适用场景 复杂策略、高频 因子研究、低频
扩展性

避坑指南:我曾经在因子研究阶段就用了事件驱动,结果每天等回测结果等到下班。后来改成向量化,同样的工作10分钟搞定。记住:研究阶段用向量化,上线前用事件驱动做精细回测。

四、我的建议

如果你刚开始做回测系统,我建议这样:

  1. 先用向量化快速验证想法。写个几十行的脚本,看看因子有没有预测能力。
  2. 想法成熟了,再上事件驱动。加入滑点、手续费、流动性限制,看看策略在真实环境下的表现。
  3. 两个系统并存。向量化做研究,事件驱动做验证。我自己的系统就是双引擎架构。

其实说白了,没有完美的架构,只有适合你的架构。关键是要理解每种方式的优缺点,然后根据你的策略类型和开发阶段来选。

记住:回测系统的目标不是模拟得越真实越好,而是在「真实度」和「开发效率」之间找到平衡点。过度追求真实,可能让你在开发阶段就放弃了。

专注资料整理