Python 量化数据存储
策略研究要反复使用同一批数据,每次都从数据源现下载既慢又不稳定——限流、断网、接口改版都会打断工作。
把数据落地到本地是量化工程的标配。
本篇实测对比 CSV、SQLite、Parquet 三种主流方案,给出选型建议。
存储方案对比
三种方案代表了三种典型思路:
| 方案 | 形式 | 优势 | 局限 | 适合场景 |
|---|---|---|---|---|
| CSV | 纯文本表格 | 任何工具都能打开,零依赖 | 体积大、读写慢、无类型 | 小数据、需要人工查看 |
| SQLite | 单文件数据库 | 支持 SQL 查询、条件过滤、事务 | 不支持并发写 | 结构化行情库、按需查询 |
| Parquet | 列式二进制 | 压缩率高、列裁剪读取快 | 不可直接人工查看 | 大体量历史数据、批量研究 |
生产环境中常见的 MySQL、ClickHouse 等数据库,思路与 SQLite/Parquet 一脉相承,个人研究用后两者足够。
CSV:最通用的交换格式
pandas 读写 CSV 只需要两个函数:
实例
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 两个函数:
实例
# 连接数据库文件(不存在则自动创建)
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 是列式压缩存储格式,按列组织数据,体积小、读取快,是数据科学领域事实上的标准格式。
实例
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 每日收盘后执行),就得到了一个自动更新的本地行情库——这也是后面策略研究的数据基础。
点我分享笔记