5、策略回测框架搭建:Python环境配置、数据获取与清洗

做市策略能不能赚钱,不能光靠拍脑袋。你得跑回测。

回测框架,说白了就是一台时光机。你把历史数据喂进去,让策略在过去的市场里跑一遍,看看它表现如何。我见过太多人,策略逻辑写得天花乱坠,一跑回测就露馅。嗯,框架搭得稳,后面才能少踩坑。

5.1 环境配置:别让工具拖后腿

我个人习惯用 Anaconda 来管理 Python 环境。为什么?省心。你想想看,做量化交易要装一堆库:pandas、numpy、matplotlib、scipy……手动装容易版本冲突,Anaconda 一键搞定。

核心依赖库清单:

  • pandas:数据处理的核心,没有它寸步难行
  • numpy:数值计算,回测里算收益率、波动率全靠它
  • matplotlib / plotly:可视化,看曲线、看分布
  • scipy:统计检验、优化算法
  • statsmodels:时间序列分析,做平稳性检验

安装命令很简单,一行搞定:

conda create -n market_making python=3.9
conda activate market_making
pip install pandas numpy matplotlib scipy statsmodels

我的小习惯:每次新建项目,我都会用 conda env export > environment.yml 把环境锁住。这样换机器、换同事,一秒钟复现环境。曾经有个项目,就因为环境不一致,回测结果差了 20%,排查了三天……

5.2 数据获取:从哪里来,到哪里去

做市策略对数据的要求很高。你想想看,做市商赚的是买卖价差,数据精度不够,回测就是自欺欺人。

我个人推荐的数据源:

  • Level 2 行情数据:包含逐笔成交、十档盘口,做市策略必备
  • 分钟级 K 线:用于计算波动率、流动性指标
  • 订单簿快照:记录每个时间点的买卖挂单情况

数据获取的代码框架:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

def fetch_tick_data(symbol, start_date, end_date):
    """
    获取逐笔成交数据
    实际项目中会连接数据库或API
    """
    # 这里模拟数据加载
    # 真实场景:从 ClickHouse / InfluxDB 读取
    df = pd.read_parquet(f'data/{symbol}_tick.parquet')
    
    # 过滤时间范围
    mask = (df['timestamp'] >= start_date) & (df['timestamp'] <= end_date)
    return df.loc[mask].reset_index(drop=True)

def fetch_orderbook_snapshot(symbol, date):
    """
    获取订单簿快照
    做市策略的核心输入
    """
    # 实际项目中,订单簿数据量很大
    # 建议按天分片存储
    return pd.read_parquet(f'data/{symbol}_orderbook_{date}.parquet')

避坑指南:我曾经遇到过数据时间戳不对齐的问题。交易所的时钟和本地时钟有偏差,回测时看起来是同一时刻,实际差了 200 毫秒。做市策略对时间敏感,200ms 足以让策略误判。解决方案:统一使用交易所时间戳,不要用本地时间。

5.3 数据清洗:脏数据是回测的毒药

数据拿到手,别急着跑策略。先洗一洗。

我总结了一套「四步清洗法」:

  1. 去重:同一笔成交被记录两次,收益率直接翻倍
  2. 去异常:价格超过 3 个标准差,大概率是错误数据
  3. 补缺失:做市策略不能有空窗期,需要前向填充
  4. 对齐时间:不同数据源的时间戳统一到毫秒级

代码实现:

def clean_tick_data(df):
    """
    清洗逐笔成交数据
    """
    # 第一步:去重
    df = df.drop_duplicates(subset=['timestamp', 'price', 'volume'])
    
    # 第二步:去异常
    # 做市策略对价格敏感,异常价格会导致虚假盈亏
    mean_price = df['price'].mean()
    std_price = df['price'].std()
    df = df[(df['price'] > mean_price - 3*std_price) & 
            (df['price'] < mean_price + 3*std_price)]
    
    # 第三步:排序并重置索引
    df = df.sort_values('timestamp').reset_index(drop=True)
    
    # 第四步:检查时间连续性
    # 如果两个 tick 之间间隔超过 5 秒,标记为异常
    df['time_diff'] = df['timestamp'].diff().dt.total_seconds()
    df['gap_flag'] = df['time_diff'] > 5
    
    return df

5.4 核心数据结构:回测的骨架

数据清洗完了,得组织成回测能用的结构。我个人习惯用 OHLCV + 订单簿 的组合。

字段 类型 说明
timestamp datetime 时间戳,精确到毫秒
open float 开盘价
high float 最高价
low float 最低价
close float 收盘价
volume int 成交量
bid_price_1 float 买一价
bid_volume_1 int 买一量
ask_price_1 float 卖一价
ask_volume_1 int 卖一量

经验之谈:做市策略回测,千万别只用 K 线数据。K 线是聚合后的,丢失了盘口深度信息。我早期犯过这个错,回测曲线漂亮得很,实盘一跑就亏。后来加上订单簿数据,回测和实盘的拟合度从 60% 提升到了 85%。

5.5 知识体系总览

下面这张图,是我对本章内容的总结。你把它存下来,后面每学一章,都可以往这张图上加内容。

策略回测框架搭建:核心流程 环境配置 数据获取 数据清洗 Anaconda 依赖库 环境锁定 Level 2 K线数据 订单簿快照 去重 去异常 补缺失+对齐 回测就绪数据 三个环节环环相扣,缺一不可 数据质量决定回测可信度

框架搭好了,数据也洗干净了。下一步就是写回测引擎的核心逻辑——订单管理、撮合规则、资金计算。这些内容,我们下一章再聊。


无相订单流研究社 微信Lucian808555