2. 回测系统搭建:回测框架选型、数据源获取与本地数据库搭建

做量化交易,说白了就是跟历史数据打交道。你想想看,没有一套靠谱的回测系统,策略好坏全靠猜,那跟赌博有什么区别?

这一章,我带你亲手搭一套回测基础设施。别怕,没那么玄乎。我们分三步走:选框架、找数据、建库房。

2.1 回测框架选型:Backtrader vs Zipline

选框架这事儿,我踩过不少坑。先说结论:个人项目、中小型策略,无脑选 Backtrader。为什么?

对比维度 Backtrader Zipline
上手难度 低,文档清晰,社区活跃 中高,依赖复杂,配置繁琐
数据接口 灵活,支持自定义数据源 强依赖 Bundle,扩展稍麻烦
性能 单线程,适合日频/小时频 支持多标的,但内存占用高
实盘对接 有现成 Broker API 封装 需要自己写适配层
维护状态 持续更新,Python 3 友好 Quantopian 关闭后,社区维护

我个人习惯用 Backtrader。记得有一次,我用 Zipline 跑一个多因子策略,光是装依赖就折腾了一下午,最后还因为数据格式不对报错。换成 Backtrader,半小时就搞定了。

核心观点:框架只是工具,别在选型上内耗。Backtrader 足够覆盖 90% 的回测场景。等你真遇到性能瓶颈了,再考虑换框架也不迟。

2.2 数据源获取:Tushare vs JoinQuant

数据是回测的血液。没有干净的数据,再牛的策略也是白搭。

国内常用的数据源,我主要用两个:TushareJoinQuant(聚宽)

Tushare

  • 优点:数据全,从股票、基金到期货、期权,应有尽有。积分制,免费用户也能拿到不少数据。
  • 缺点:接口调用频率有限制,高频数据需要付费积分。数据偶尔有缺失,需要自己清洗。
  • 适用场景:个人研究、低频策略、需要多品种数据的场景。

JoinQuant(聚宽)

  • 优点:数据质量高,自带回测平台,可以直接在线跑策略。API 设计简洁,对新手友好。
  • 缺点:本地使用需要下载数据,离线支持不如 Tushare 灵活。免费版有数据量限制。
  • 适用场景:快速原型验证、在线回测、不想折腾本地环境的用户。

我的建议:如果你刚开始,先用 Tushare 的免费接口把流程跑通。等需要更高质量的数据时,再考虑 JoinQuant 或付费数据源。别一开始就追求完美,先动起来。

2.3 本地数据库搭建:SQLite

数据拿到手,总不能每次都去远程拉吧?效率低不说,万一接口挂了,你连回测都跑不了。

所以,本地数据库是必须的。我推荐 SQLite。为什么?

  • 零配置:不需要安装服务器,一个文件就是一个数据库。
  • 轻量级:适合单机回测,读写速度足够快。
  • Python 原生支持:标准库自带 sqlite3,无需额外依赖。

下面是一个简单的建表示例,用来存储日线数据:

import sqlite3

# 连接数据库(如果文件不存在,会自动创建)
conn = sqlite3.connect('backtest.db')
cursor = conn.cursor()

# 创建日线数据表
cursor.execute('''
    CREATE TABLE IF NOT EXISTS daily_data (
        code TEXT NOT NULL,        -- 股票代码
        date TEXT NOT NULL,        -- 交易日期
        open REAL,                 -- 开盘价
        high REAL,                 -- 最高价
        low REAL,                  -- 最低价
        close REAL,                -- 收盘价
        volume INTEGER,            -- 成交量
        amount REAL,               -- 成交额
        PRIMARY KEY (code, date)   -- 联合主键,防止重复数据
    )
''')

conn.commit()
conn.close()
print("数据库和表创建成功!")

注意:SQLite 不支持并发写入。如果你有多进程同时写入的需求,建议换成 PostgreSQL。不过对于个人回测,单线程写入完全够用。

我曾经犯过一个低级错误:把数据全部存在 CSV 文件里,每次回测都要全量加载。结果数据量一大,内存直接爆了。换成 SQLite 后,按日期范围查询,速度提升了不止一个量级。

2.4 知识体系总览

为了让你更直观地理解这三部分的关系,我画了一张流程图:

回测系统搭建核心流程 数据源获取 Tushare / JoinQuant 本地数据库 SQLite 存储 回测框架 Backtrader / Zipline 数据流向说明: 1. 通过 Tushare/JoinQuant API 获取原始行情数据 2. 清洗、格式化后存入本地 SQLite 数据库 3. Backtrader/Zipline 从 SQLite 读取数据执行回测 4. 回测结果输出到文件或数据库,供后续分析 建议:先跑通流程,再优化细节

2.5 实战:把 Tushare 数据存入 SQLite

光说不练假把式。下面是一个完整的示例,演示如何从 Tushare 获取数据并存入 SQLite:

import tushare as ts
import sqlite3
from datetime import datetime, timedelta

# 设置 Tushare Token(需要先在 tushare.pro 注册)
ts.set_token('你的token')
pro = ts.pro_api()

# 连接 SQLite
conn = sqlite3.connect('backtest.db')
cursor = conn.cursor()

# 获取股票日线数据
def fetch_and_store(code, start_date, end_date):
    df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
    
    if df.empty:
        print(f"未获取到 {code} 的数据")
        return
    
    # 插入数据
    for _, row in df.iterrows():
        cursor.execute('''
            INSERT OR REPLACE INTO daily_data 
            (code, date, open, high, low, close, volume, amount)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            row['ts_code'],
            row['trade_date'],
            row['open'],
            row['high'],
            row['low'],
            row['close'],
            row['vol'],
            row['amount']
        ))
    
    conn.commit()
    print(f"{code} 数据已存入,共 {len(df)} 条记录")

# 示例:获取贵州茅台最近30天数据
fetch_and_store('000001.SZ', '20240101', '20240130')

conn.close()

小技巧:第一次全量拉取历史数据时,建议分批处理。比如每次拉取一年数据,避免接口超时。我一般会写个循环,从上市日期开始,逐年拉取。

嗯,到这里,回测系统的基础设施就搭好了。你有了数据源,有了本地数据库,也有了回测框架。接下来,就可以开始写策略了。

记住,这套流程我用了好几年,虽然简单,但足够稳定。别小看这些基础工作,它们决定了你后续所有策略研究的效率。

专注资料整理