Python 量化数据存储

策略研究要反复使用同一批数据,每次都从数据源现下载既慢又不稳定——限流、断网、接口改版都会打断工作。

把数据落地到本地是量化工程的标配。

本篇实测对比 CSV、SQLite、Parquet 三种主流方案,给出选型建议。


存储方案对比

三种方案代表了三种典型思路:

方案 形式 优势 局限 适合场景
CSV 纯文本表格 任何工具都能打开,零依赖 体积大、读写慢、无类型 小数据、需要人工查看
SQLite 单文件数据库 支持 SQL 查询、条件过滤、事务 不支持并发写 结构化行情库、按需查询
Parquet 列式二进制 压缩率高、列裁剪读取快 不可直接人工查看 大体量历史数据、批量研究

生产环境中常见的 MySQL、ClickHouse 等数据库,思路与 SQLite/Parquet 一脉相承,个人研究用后两者足够。


CSV:最通用的交换格式

pandas 读写 CSV 只需要两个函数:

实例

import yfinance as yf
import pandas as pd

# 下载茅台日线(未复权,含复权因子调整列)
df = yf.download("600519.SS", start="2019-01-01", end="2024-12-31",
                 auto_adjust=False, progress=False, multi_level_index=False)

# 保存:index=True 把日期索引一并写入
df.to_csv("maotai.csv")

# 读取:parse_dates 把日期列还原为时间索引
back = pd.read_csv("maotai.csv", index_col="Date", parse_dates=True)
print(back.tail(2).round(2).to_string())

易错点:读取 CSV 时必须指定 index_col 与 parse_dates,否则日期会变成普通字符串列,时间序列的切片、重采样全部失效。这是 CSV 方案最常见的翻车点。


SQLite:可查询的本地数据库

SQLite 是嵌入在单文件里的关系数据库,Python 标准库自带驱动,无需安装任何服务。

pandas 与它配合只需 to_sql 和 read_sql 两个函数:

实例

import sqlite3

# 连接数据库文件(不存在则自动创建)
conn = sqlite3.connect("maotai.db")

# 写入:DataFrame 变成一张表
df.to_sql("maotai", conn, if_exists="replace")

# 用 SQL 做条件查询:只取 2024 年的数据
back = pd.read_sql(
    "SELECT * FROM maotai WHERE Date >= '2024-01-01'",
    conn, index_col="Date", parse_dates={"Date"})
print("查询返回行数:", len(back))

# SQL 聚合:直接在库里算月均收盘价
q = pd.read_sql(
    "SELECT strftime('%Y-%m', Date) AS 月份, "
    "ROUND(AVG(Close), 2) AS 月均收盘 "
    "FROM maotai GROUP BY 月份 ORDER BY 月份 DESC LIMIT 3", conn)
print(q.to_string(index=False))

conn.close()

输出结果如下所示:

查询返回行数: 241
 月份   月均收盘
2024-12  1533.16
2024-11  1554.50
2024-10  1565.79

SQL 的价值在于"取所需":只查某年、某几列、某种聚合,不必把整张表读进内存。数据量上到几十万行后,这个优势会非常明显。


Parquet:面向研究的列式存储

Parquet 是列式压缩存储格式,按列组织数据,体积小、读取快,是数据科学领域事实上的标准格式。

实例

# 写入 Parquet(需 pyarrow:pip install pyarrow)
df.to_parquet("maotai.parquet")

# 读取
back = pd.read_parquet("maotai.parquet")
print(back.shape)

三种方案实测对比

口说无凭,用茅台 2019-2024 共 1453 行 × 6 列的日线数据,实测三种方案的体积与速度(本机实测,绝对值因机器而异):

方案 写入耗时 读取耗时 文件体积
CSV 0.00 秒 0.003 秒 125 KB
SQLite 0.00 秒 0.002 秒(含日期过滤) 152 KB
Parquet 1.57 秒 1.29 秒 71 KB

结论可能和直觉相反:小数据集上三者都够快,Parquet 反而最慢——列式引擎的初始化开销在小文件上得不偿失。

Parquet 的两项真实优势在数据变大后才显现:

一是体积:本例已比 CSV 小 43%,多标的长历史的数据集上压缩比还会更高。

二是列裁剪:只读两列时 Parquet 不必扫全表,数据越大节省越多。

选型建议:个人量化研究推荐 SQLite 做主存储(可查询、可增量)+ Parquet 做批量研究的归档格式;CSV 只用于与他人交换数据。等单表数据超过千万行,再考虑 ClickHouse 等专业时序方案。


增量更新思路

数据落地后还要保持新鲜。比起每次全量重写,增量追加更高效,核心逻辑是三步:

步骤 操作
查最新日期 SELECT MAX(Date) FROM 表名
下载缺口 数据源 start 参数设为最新日期的次日
追加写入 df.to_sql(..., if_exists="append")

把这个流程封装成定时任务(如 crontab 每日收盘后执行),就得到了一个自动更新的本地行情库——这也是后面策略研究的数据基础。