3. 数据源与数据加载:本地CSV数据加载、在线数据API(Yahoo/Quandl)、数据清洗与预处理
在量化回测中,数据是策略的“燃料”。Backtrader 提供了灵活的数据加载机制,支持从本地文件、在线 API 以及自定义数据源获取历史行情。本章将系统讲解三种主流数据加载方式,并重点介绍数据清洗与预处理的最佳实践,确保回测结果的准确性与可靠性。
3.1 数据源概览与选择标准
不同的数据源在覆盖范围、更新频率、历史深度和成本上存在显著差异。选择合适的数据源是回测工作的第一步。
| 数据源类型 | 典型代表 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 本地 CSV | 自备历史数据文件 | 离线可用、速度快、数据完全可控 | 需要自行维护更新、历史深度有限 | 策略开发、离线回测、数据敏感场景 |
| 在线 API | Yahoo Finance, Quandl | 实时更新、历史数据丰富、无需本地存储 | 依赖网络、有请求频率限制、可能停服 | 快速原型验证、实时策略、研究分析 |
| 数据库/专业终端 | MySQL, Wind, Bloomberg | 高可靠性、数据质量高、支持高频 | 成本高、配置复杂、学习曲线陡 | 机构级回测、生产环境 |
建议: 初学者优先使用本地 CSV 进行策略开发,待逻辑验证通过后,再切换到在线 API 或专业数据源进行扩展测试。
3.2 本地 CSV 数据加载
CSV 是最通用的数据交换格式。Backtrader 内置了 GenericCSVData 类,可以快速加载标准格式的 CSV 文件。
3.2.1 标准 CSV 格式要求
Backtrader 默认期望 CSV 文件包含以下列(按顺序):
- Date:日期,格式如
YYYY-MM-DD - Open:开盘价
- High:最高价
- Low:最低价
- Close:收盘价
- Volume:成交量
- OpenInterest:持仓量(可选,可设为 0)
3.2.2 加载 CSV 的核心代码
import backtrader as bt
# 创建 Cerebro 引擎
cerebro = bt.Cerebro()
# 加载本地 CSV 数据
data = bt.feeds.GenericCSVData(
dataname='./data/AAPL.csv', # 文件路径
dtformat='%Y-%m-%d', # 日期格式
timeframe=bt.TimeFrame.Days, # 时间周期
compression=1, # 压缩倍数
fromdate=datetime(2020, 1, 1), # 起始日期
todate=datetime(2023, 12, 31) # 结束日期
)
# 将数据添加到引擎
cerebro.adddata(data)
3.2.3 自定义列映射
如果 CSV 文件的列名或顺序与默认不同,可以通过参数进行映射:
data = bt.feeds.GenericCSVData(
dataname='./data/my_data.csv',
datetime=0, # 日期列索引
open=1, # 开盘价列索引
high=2, # 最高价列索引
low=3, # 最低价列索引
close=4, # 收盘价列索引
volume=5, # 成交量列索引
openinterest=-1 # 无持仓量列
)
注意: 如果 CSV 文件包含表头行,Backtrader 会自动跳过。若文件无表头,需设置
header=None 参数。
3.3 在线数据 API 加载
Backtrader 通过扩展数据源支持在线 API。Yahoo Finance 和 Quandl 是最常用的两个免费数据源。
3.3.1 Yahoo Finance 数据加载
使用 YahooFinanceData 需要安装 yfinance 库:
pip install yfinance
加载示例:
import backtrader as bt
cerebro = bt.Cerebro()
# 从 Yahoo Finance 加载数据
data = bt.feeds.YahooFinanceData(
dataname='AAPL', # 股票代码
fromdate=datetime(2020, 1, 1),
todate=datetime(2023, 12, 31),
buffered=True # 启用缓冲,提高性能
)
cerebro.adddata(data)
3.3.2 Quandl 数据加载
Quandl 提供更专业的数据集,需要 API Key:
data = bt.feeds.Quandl(
dataname='WIKI/AAPL', # Quandl 代码
fromdate=datetime(2020, 1, 1),
todate=datetime(2023, 12, 31),
apikey='YOUR_API_KEY' # 替换为实际 Key
)
性能提示: 在线 API 受网络延迟和请求频率限制影响。建议在回测前将数据缓存到本地 CSV,避免重复请求。
3.4 数据清洗与预处理
原始数据通常包含缺失值、异常值、除权除息缺口等问题,必须进行清洗才能用于回测。
3.4.1 常见数据问题与处理方法
| 问题类型 | 表现 | 处理方法 | Backtrader 实现 |
|---|---|---|---|
| 缺失值 | NaN 或空行 | 前向填充、插值、删除 | data.resample() 或预处理 |
| 除权除息缺口 | 价格跳空 | 使用复权数据 | 加载前使用复权 CSV |
| 异常价格 | 价格超出合理范围 | 设置上下限、剔除 | 自定义过滤器 |
| 非交易日数据 | 周末/节假日有数据 | 过滤非交易日 | bt.filters.SessionFilter |
3.4.2 使用 Pandas 进行预处理
推荐在加载到 Backtrader 之前,使用 Pandas 完成数据清洗:
import pandas as pd
# 读取原始 CSV
df = pd.read_csv('raw_data.csv', parse_dates=['Date'], index_col='Date')
# 1. 处理缺失值:前向填充
df.fillna(method='ffill', inplace=True)
# 2. 去除异常值(例如:价格低于 0)
df = df[df['Close'] > 0]
# 3. 确保时间序列连续
df = df.asfreq('B') # 转换为工作日频率
# 4. 保存清洗后的数据
df.to_csv('cleaned_data.csv')
3.4.3 Backtrader 内置过滤器
Backtrader 提供了一些内置过滤器,可以在数据加载时直接应用:
# 使用 SessionFilter 过滤非交易日
data.addfilter(bt.filters.SessionFilter)
# 使用 Heikin-Ashi 过滤器(平滑价格)
data.addfilter(bt.filters.HeikinAshi)
3.5 数据加载流程架构
下图展示了从原始数据到回测引擎的完整数据流:
3.6 多数据源混合加载
实际回测中常需要同时使用多个数据源。Backtrader 支持在同一 Cerebro 引擎中混合加载不同来源的数据:
# 混合加载示例
cerebro = bt.Cerebro()
# 从 CSV 加载股票 A
data_stock = bt.feeds.GenericCSVData(dataname='stock_A.csv')
cerebro.adddata(data_stock)
# 从 Yahoo 加载指数
data_index = bt.feeds.YahooFinanceData(dataname='^GSPC')
cerebro.adddata(data_index)
# 从 Quandl 加载期货
data_futures = bt.feeds.Quandl(dataname='CHRIS/CME_CL1')
cerebro.adddata(data_futures)
最佳实践: 将不同数据源的数据统一清洗为相同格式(列名、频率、时区),再加载到 Backtrader 中,可以大幅减少后续策略开发中的兼容性问题。
3.7 数据质量检查清单
在开始回测前,建议逐项检查数据质量:
- 时间范围:确认数据覆盖了策略所需的完整时间段
- 缺失比例:缺失值占比不应超过 5%,否则需考虑数据源替代
- 价格合理性:检查是否存在负价格、极端跳空等异常
- 成交量验证:成交量不应长期为 0(停牌期间除外)
- 复权一致性:确保所有品种使用相同的复权方式(前复权/后复权)
- 时间戳对齐:多品种数据的时间戳必须对齐到同一时区
通过系统化的数据加载与清洗流程,可以确保回测结果建立在可靠的数据基础之上,避免“垃圾进,垃圾出”的陷阱。