Data · OHLCV 接入

Backtesting.py 数据要求:单资产 OHLCV、数据源接入与质量自检

Backtesting.py 对数据的要求很小:一张以时间为索引、含 Open / High / Low / Close(Volume 可选)列的 pandas DataFrame,就能驱动一次回测。真正花时间的不是「怎么喂数据」,而是把数据源口径、列名大小写、时间对齐与复权口径统一——这些做错不会报错,只会让结论悄悄失真。本页按「格式要求 → 数据源对照 → 分步接入 → 检查清单 → 质量排查」整理。

格式:OHLC(V) DataFrame索引:时间粒度:单资产
OHLC 四列开高低收必需
DatetimeIndex时间作索引
Volume 可选无量能可省略
单资产一次一条序列
Backtesting.py 对行情数据的要求示意(基于官方 README):列名、时间索引与单资产边界是本页反复强调的三件事。
Format

OHLCV 数据格式要求

数据格式是硬要求:列名大小写、索引类型、是否单序列,任何一项不符都会在运行时报错或静默出错。下表把每一项的作用与「不满足时会怎样」放在一起看。

是否必需作用不满足时的现象注意点
Open必需开盘价,撮合与成交价假设的基准之一运行时报列缺失类错误大小写敏感,需与官方要求完全一致
High必需区间内的价格高点,用于止损止盈与波动度量同上;部分策略逻辑失效必须 ≥ Low,需自查高低价关系
Low必需区间内的价格低点同上同上
Close必需收盘价,多数指标与信号的输入指标算不出或全为空值指标基于收盘价计算时尤其关键
Volume可选成交量,量能类策略与指标需要依赖量能的策略无法运行或全为空没有量能数据时省略即可,不要填 0 冒充
DatetimeIndex必需时间轴,决定 bar 顺序与逐 bar 迭代列名对但顺序错,结果失真且不报错须为时间类型且单调递增
单条价格序列必需一次回测对应一个标的的价格序列多标的宽表会被当成一列多值处理多标的需分别回测后自行汇总
自定义附加列可选预先把外部指标算好挂进 DataFrame不影响运行,但会增加数据维护成本列名不要与 OHLC 冲突
import pandas as pd
# 目标形态:index = DatetimeIndex,列 = Open/High/Low/Close[/Volume]
df = pd.DataFrame(raw, columns=['Open', 'High', 'Low', 'Close', 'Volume'])
df.index = pd.to_datetime(df.index)
df = df.sort_index()
口径提醒:列名与必需性以官方 README 与文档为准;不同数据源返回的中文列名、代码列、复权列都需要你自己重命名与裁剪,Backtesting.py 不会替你猜列名。
Sources

数据源对照:几种取数方式怎么选

Backtesting.py 只内置一份示例数据用于跑通流程;真实研究的数据要靠你自己准备。下表按「取数方式 → 覆盖范围 → 适用场景 → 限制与注意点」对照常见来源。

取数方式覆盖范围适用场景限制与注意点
内置示例数据官方随包提供的一只美股示例跑通流程、验证代码路径只适合试跑,不能用于任何绩效结论
本地 CSV / Parquet + pandas取决于你的数据文件离线复现、归档、内网环境需自行整理列名与时间索引,并记录数据来源与采集日期
tushare 等 A 股接口A 股行情与部分基本面国内 A 股日线级研究通常需要 Token 与积分,字段名与限频以数据源官方文档为准
akshare 等开源接口A 股、基金、期货、指数等多类数据个人研究、快速取数接口稳定性与字段随上游变化,取数后必须做质量自检
yfinance 等海外接口美股等海外市场海外标的研究境内网络可达性不稳定,字段与复权口径以数据源文档为准
券商 / 终端导出与你账户权限一致的市场已付费数据的复现研究导出格式各异,注意时间字段与时区
合成数据(随机游走等)无真实市场含义调试指标与信号逻辑不能用于任何绩效结论,仅验证代码是否正确
免责与边界:各数据源的字段名、是否收费、是否需要 Token、限频规则,以该数据源官方文档为准;本页不承诺任何数据源的可用性、完整性与实时性,取数失败与合规责任由使用者自行判断。
Prepare

接数据到回测:五步实操

把数据交给 Backtesting.py 的五步按顺序做,每步给出真实代码、一句话说明与可验证的预期结果。以一张本地 CSV 为例,换成接口取数时只需替换取数那一步。

  1. 读入原始数据

    先用 pandas 把数据读进来看看长什么样:字段名、时间列位置、是否有代码列。先观察再改名,能省掉后面反复对不上的时间。

    import pandas as pd
    raw = pd.read_csv('600519.csv')
    print(raw.head())
    print(raw.columns.tolist())

    预期输出:前几行数据与列名清单。若时间列是字符串、或混着股票代码列,下一步统一处理。

  2. 统一列名与时间索引

    把列名重命名为 Open/High/Low/Close(Volume 可选),并把时间列设为 DatetimeIndex——这一步决定了后面所有指标与信号能不能算。

    df = raw.rename(columns={'日期': 'index', '开盘': 'Open', '高点': 'High',
                             '低点': 'Low', '收盘': 'Close', '成交量': 'Volume'})
    df['index'] = pd.to_datetime(df['index'])
    df = df.set_index('index').sort_index()
    print(df.dtypes)

    预期输出:各列为数值类型、索引为时间类型。若价格为字符串,需要先转数值再继续。

  3. 做一次质量自检

    在把数据交给任何回测之前先跑一遍检查:缺失值、重复时间、价格是否为 0 或负、时间是否单调。数据问题越早发现,返工成本越低。

    print(df.index.is_monotonic_increasing, df.index.duplicated().sum())
    print(df[['Open', 'High', 'Low', 'Close']].isna().sum().to_dict())
    print(df.index.min(), df.index.max())

    预期输出:True / 0 / 各列缺失数全为 0 / 起止日期覆盖你要回测的区间。任一项不符先回到上一步修数据。

  4. 确认单资产与区间

    这个库一次回测对应一条价格序列:多标的要先拆开分别回测,不要塞进一张宽表。同时确认区间足够长,能覆盖不同市场环境。

    price = df['Close']
    print(len(price), price.index[0], price.index[-1])

    预期输出:样本条数与首尾日期。样本过少时,任何指标结论都不可信(区间选择标准以你自己的研究设计为准)。

  5. 交给 Backtesting.py 并核对

    把 DataFrame 按位置传给回测对象(它是回测的必要输入),跑一次最小回测确认数据能被正确读取,再进入策略与参数环节。

    from backtesting import Backtest
    from backtesting.test import SMA
    
    bt = Backtest(df, MyStrategy, cash=10_000, commission=.002)
    stats = bt.run()
    print(stats['# Trades'])

    预期输出:打印出成交笔数(具体数值随数据与策略变化)。若成交数为 0,先查列名、时间顺序与信号条件,而不是先怀疑策略逻辑。

Checklist

列名与索引检查清单:喂数据前跑一遍

下面这些检查都是 pandas 通用写法,不需要额外依赖;建议固化成脚本,每次换数据源都跑一次。表里的「预期结果」是基本要求,不是充分条件。

检查项怎么查预期结果不合格怎么处理
列名是否齐全且大小写正确df.columns.tolist()含 Open/High/Low/Close(Volume 视情况)用 rename 统一列名,不要用大小写不一致的列
索引是否为时间类型df.index.dtypedatetime64 类时间类型pd.to_datetime() 转换后再设为索引
时间是否单调递增df.index.is_monotonic_increasingTruesort_index() 重新排序;乱序会让回测结果失真
是否有重复时间点df.index.duplicated().sum()0定位重复行后去重,并确认数据源是否分多次拼接
关键列是否有缺失值df[['Open','High','Low','Close']].isna().sum()全为 0先确认缺失成因(停牌/未收盘/接口缺失),再决定删除或补齐
价格是否为 0 或负值(df[['Open','High','Low','Close']] <= 0).sum()全为 0多为复权或字段错位导致,需回数据源核对口径
高低价关系是否成立(df['High'] < df['Low']).sum()0列错位或数据源异常,需回源核对,不能靠数值变换掩盖
时间区间是否覆盖回测区间df.index.min(), df.index.max()覆盖你设定的起止日期补齐区间或缩小回测区间,避免「样本外其实是空」
成交量量级是否合理df['Volume'].describe()(若有)量级与标的规模相符,无异常 0 值量能缺失时宁可不做量能类策略,也不要填 0
使用提醒:以上是通用数据检查,不是 Backtesting.py 的官方校验流程;官方对数据的要求以 README 与文档描述为准,检查项的具体写法以 pandas 官方文档为准。
Quality

数据质量排查:八种常见情况

Backtesting.py 里数据问题最麻烦的地方在于「不报错但结论错」。下表按「现象 → 成因 → 处理方式 → 注意点」整理,处理顺序建议是先对齐时间轴、再处理缺失、最后才计算指标与信号。

现象成因处理方式注意点
回测区间内出现大段空白停牌或数据源本身缺失交易日确认是真实停牌还是漏数据,再决定保留缺口或剔除区间不要用「向前填充价格」掩盖停牌,会伪造出可成交的假象
指标跳空、收益率异常复权口径不一致(不复权/前复权/后复权混用)同一回测内统一口径,并在记录里写明用的是哪种复权算法与字段名以数据源官方文档为准
分红送转日价格断崖未复权导致价格跳空改用复权数据,或明确接受不复权口径并在结论里说明口径不同会让收益率与回撤都不可比
把多个标的塞进一张表后结果混乱Backtesting.py 一次回测对应单资产价格序列拆成多份单资产数据,分别回测后自行汇总多资产组合能力不在这个库的能力范围内
策略在历史上表现异常好用了当时不可能知道的信息(未来数据)检查信号是否用到当日收盘后才知道的数据,必要时整体后移一根 bar这是最容易被忽略、也最伤结论的一类错误
依赖量能的策略没有信号Volume 列缺失或全为 0补齐量能数据,或换不依赖量能的策略Volume 本就可选,缺它就别用需要它的指标
时间对不上、交易日错位时区或时间戳格式不一致统一到同一时区与同一时间粒度后再对齐跨市场数据尤其容易踩这一条
样本区间太短只用了一小段行情拉长区间覆盖不同市场环境,或明确标注结论适用范围短样本下的参数结论通常不可复述
Timeframe

时间粒度:日线、日内与多频率

Backtesting.py 以逐 bar 迭代运行,bar 的粒度完全由你喂进来的数据频率决定——也就是说,能不能做日内回测,取决于你手上有没有分钟级数据。

日线:最常见也最稳

日线 OHLCV 是绝大多数中低频策略的默认选择:数据易得、噪声相对小,成交价假设的争议也最少。缺点是信号滞后一天,对短线策略的刻画能力有限。

日内:粒度越细,假设越关键

把分钟级 OHLCV 喂进来就能做日内回测,但成交时点、滑点与撮合假设的重要性会急剧上升;同一份数据换一个成交价假设,结论可能完全不同,需谨慎验证。

多频率组合:要自己对齐

在策略里同时用日线与更长周期指标是可行的,但不同频率的数据对齐、重采样与「哪一根 bar 才可见」都需要你自己处理,Backtesting.py 不会替你完成频率转换。

边界:数据频率决定回测粒度,日内回测对数据质量与成交时点假设更敏感;本站不提供任何数据源,也不对回测结论作任何保证,结果仅供研究,不构成投资建议。
Pitfalls

喂数据时最容易踩的六类问题

把真实数据接进 Backtesting.py 时,下面这些现象出现频率较高。按「现象 → 可能原因 → 处理 → 提前规避」四列整理,报错原文以官方与解释器实际提示为准。

现象可能原因处理提前规避
报列缺失类错误列名大小写不一致或缺列按官方要求统一为 Open/High/Low/Close取数后立刻打印列名清单核对
指标全是空值、没有成交窗口长度超过数据长度或关键列有缺失检查窗口与数据长度、补齐缺失值先跑一次最小回测确认数据可用
结果明显好得不真实前视偏差、区间过短或成本未计入检查信号可见性、拉长区间、补上手续费与滑点把成本假设固定写进回测参数并留档
换一段数据结论就翻转参数是在同一段历史上挑出来的做样本内外的切分再复核数据准备好当天就规划好样本切分方式
图表打不开或显示异常可视化依赖缺失或版本不匹配按官方安装说明补齐依赖后重试先把依赖装全再开始研究,避免中途卡住
同一份数据两次结果不同数据文件被更新、或时间顺序被改动固定数据快照并记录采集日期首次取数成功后就落盘保存,回测只读本地文件
FAQ

数据常见问题

数据列名必须大写吗?

是,这个库按 Open/High/Low/Close(Volume 可选)这样的大小写识别列;写成小写或中文列名会导致运行时报列缺失。具体列名要求以官方 README 与文档为准,其余自定义列可以保留但要避免与 OHLC 冲突。

Volume 可以省略吗?

可以,Volume 是可选的:不依赖量能的指标与策略完全可以不带这一列。但要注意两点——不要用 0 填充冒充「无成交量」,也不要省略后仍去用需要量能的指标,否则指标会算不出来或全为空值。

能直接接 tushare、akshare 这类 A 股接口吗?

可以,思路统一:接口取回数据后,把列名改成 Open/High/Low/Close(量能按需保留)、把时间列转成 DatetimeIndex 并排序,就能传进回测。但各接口的字段名、复权口径、是否需要 Token 与限频规则都不一样,一律以该数据源官方文档为准;国内接口的可用性与稳定性也需你自己评估。

能一次回测多个标的一起做组合吗?

不行。Backtesting.py 一次回测对应一条价格序列,不支持多资产组合,也不支持分数股——把多只股票塞进一张宽表,会被当成结构异常的数据处理,结果不可信。正确做法是每个标的单独回测,再自行汇总组合层面的结论。

分钟级数据能做日内回测吗?

能,bar 的粒度由数据频率决定,喂分钟级数据就按分钟推进。但日内回测对成交时点、滑点与撮合假设极其敏感:同一份数据换一个成交价假设可能得出相反结论。建议先用日线验证策略逻辑,再考虑是否需要下降到日内,且结论以你自己的成本假设为准。

数据处理这块能交给本机技能路线做吗?

可以省掉一部分取数工作:本机已核验的技能里有覆盖财经行情数据与技术指标分析的方向,一句话即可发起取数与基础分析;但拿到的数据仍需你自己做字段与口径校验才能喂进回测,两者是相互独立的两条路线,没有已证实集成关系。是否值得取决于你想把时间花在数据工程还是策略本身。

数据准备好了,继续这两步

先用示例数据把整条链路跑通,再回来看策略与指标的写法,避免在数据源上卡住。