2、回测框架搭建:Python环境准备、Pandas数据处理、回测引擎核心逻辑

做市策略这东西,光看理论是没用的。你得让它跑起来,跑在真实的历史数据上,才能知道它到底是骡子是马。这一章,我们就来亲手搭一个回测框架。

我个人习惯把回测框架拆成三块:环境底座数据管道引擎核心。一块一块来,别急。

2.1 Python环境准备:别在工具上翻车

说实话,我见过太多人花了两小时调策略,结果发现是Python版本不对。嗯,这种坑我踩过不止一次。

你需要的东西其实不多:

  • Python 3.8+(我推荐3.10,稳定且兼容性好)
  • Pandas 1.5+(数据处理的主力)
  • NumPy(数值计算,Pandas的底层依赖)
  • Matplotlib(画图看回测曲线用)

装起来很简单:

pip install pandas numpy matplotlib
小技巧: 我建议你用虚拟环境。每个项目一个独立环境,互不干扰。曾经有一次我同时做两个项目,一个要Pandas 1.3,一个要1.5,结果互相覆盖,折腾了一下午。
python -m venv backtest_env
source backtest_env/bin/activate  # Mac/Linux
backtest_env\Scripts\activate     # Windows

2.2 Pandas数据处理:你的数据就是你的命

做市策略回测,数据质量直接决定结果可信度。说白了,垃圾数据进,垃圾结果出。

我们通常需要这样的数据结构:

字段 类型 说明
timestamp datetime 时间戳,精确到秒或毫秒
bid_price float 买一价
ask_price float 卖一价
bid_volume float 买一量
ask_volume float 卖一量
mid_price float 中间价(计算得出)
spread float 买卖价差(计算得出)

加载数据并做预处理,我一般这么写:

import pandas as pd
import numpy as np

def load_and_prepare_data(filepath):
    df = pd.read_csv(filepath, parse_dates=['timestamp'])
    df.set_index('timestamp', inplace=True)
    
    # 计算衍生字段
    df['mid_price'] = (df['bid_price'] + df['ask_price']) / 2
    df['spread'] = df['ask_price'] - df['bid_price']
    
    # 去掉空值
    df.dropna(inplace=True)
    
    return df
注意: 我曾经遇到过数据里时间戳不连续的情况,回测结果完全偏了。后来养成了习惯——加载数据后先检查时间间隔是否均匀。用 df.index.diff().value_counts() 看一眼,心里有底。

2.3 回测引擎核心逻辑:让策略动起来

回测引擎说白了就是一个循环——遍历每一笔行情,判断要不要下单,然后记录结果。你想想看,这不就是模拟一个交易员坐在屏幕前盯盘吗?

核心逻辑就三步:

  1. 接收行情:拿到当前时刻的买卖盘口数据
  2. 策略决策:根据当前持仓和行情,决定是否挂单、撤单
  3. 记录状态:更新持仓、资金、盈亏

我习惯把引擎封装成一个类,这样复用起来方便:

class BacktestEngine:
    def __init__(self, data, initial_capital=100000):
        self.data = data
        self.capital = initial_capital
        self.position = 0  # 当前持仓数量
        self.trades = []   # 交易记录
        self.equity_curve = []  # 权益曲线
        
    def run(self, strategy_func):
        for idx, row in self.data.iterrows():
            # 1. 策略决策
            action, price, volume = strategy_func(
                row, self.position, self.capital
            )
            
            # 2. 执行交易
            if action == 'buy':
                cost = price * volume
                if cost <= self.capital:
                    self.capital -= cost
                    self.position += volume
                    self.trades.append({
                        'time': idx, 'type': 'buy',
                        'price': price, 'volume': volume
                    })
            elif action == 'sell':
                if volume <= self.position:
                    self.capital += price * volume
                    self.position -= volume
                    self.trades.append({
                        'time': idx, 'type': 'sell',
                        'price': price, 'volume': volume
                    })
            
            # 3. 记录权益
            current_equity = self.capital + self.position * row['mid_price']
            self.equity_curve.append(current_equity)
        
        return self.calculate_performance()
核心要点: 回测引擎的「真实感」取决于你如何处理滑点和成交概率。做市策略尤其敏感——你挂单不一定能成交。我一般会在引擎里加一个「成交概率模型」,比如价差在1个tick以内时成交概率80%,超过2个tick就降到30%。这样回测结果更贴近实盘。

2.4 知识体系一览

为了让你对整个框架有个直观印象,我画了一张图:

回测框架核心结构 环境层 Python 3.8+ | Pandas | NumPy | Matplotlib 数据层 行情加载 → 清洗 → 衍生字段计算 → 时间对齐 引擎层(核心循环) 行情接收 → 策略决策 → 执行交易 → 状态记录 滑点模型 | 成交概率 | 资金管理 输出层:绩效指标 | 权益曲线 | 交易记录

你看,整个框架其实不复杂。环境搭好,数据准备好,引擎写个循环,就齐活了。但真正让回测有价值的,是你在每个环节里加入的「真实感」——比如滑点、成交概率、手续费。这些细节,才是区分业余和专业的分水岭。

我的经验: 刚开始做回测时,我总想一次性把引擎写得特别完美。结果写了三天还没跑通。后来我学乖了——先写一个最简版本,能跑通就行。然后一点点加功能。这叫「渐进式开发」,效率高得多。

好了,环境搭好了,数据也准备好了,引擎的核心逻辑也清楚了。接下来就是让策略真正跑起来,看看它到底能赚多少、亏多少。这部分,我们留到下一节细聊。


公众号:蓝海资料掘金营,微信deep3321