行情工程实战 M01|存储选型 CSVSQLiteMySQL

【Python行情API量化工程实战 M01】数据存哪里不卡?Python股票存储方案 CSV-SQLite-MySQL 对比与选型

本文是「Python 行情 API 量化工程实战」系列第 1 篇(工程化模块)。做量化的第一步往往是把行情数据拉下来------但拉下来塞哪儿?扔进内存下次启动就没了,写成 CSV 文件又慢又占盘。这篇把量化项目最常见的三种本地存储方案放在同一台机器、同一份数据上跑了一次真实基准,看完你就能根据自己的场景挑对方案。

本文你将得到什么

  1. 三种存储方案在同一台机器同一份数据下的写入 / 全量读 / 条件查询耗时对比
  2. CSV / SQLite / MySQL 选型决策树(10 万行以下?百万行?千万行?多人协作?)
  3. SQLite 的一行代码增量更新写法:用 UNIQUE(code, trade_dt) 避免重复写
  4. 三种方案的 5 个常见坑(编码、并发、列类型、磁盘 IO、备份策略)
  5. 一个可直接套用的「拉数 → 落库」最小工程骨架(零第三方 SDK,纯 HTTP)

一、数据从哪来(最小依赖)

本文不依赖任何第三方行情 SDK------所有数据通过一个官方演示证书直接 HTTP GET 获取(返回的是预先设置的演示数据,需替换为自己的实际证书),零安装、跨语言。你只需要:

bash 复制代码
pip install requests   # 仅此一个依赖,标准库 csv / sqlite3 自带

运行环境:

  • Python 3.9+(标准库即可,无需 pandas)
  • requests 2.x
  • SQLite 3.x(Python 标准库自带,无需安装)
  • MySQL(可选,需自行部署;本文 DDL 部分给参考,未连库实测)
python 复制代码
import requests

# 演示证书(官方演示证书,返回的是预先设置的演示数据,需要替换为自己的实际证书)
TOKEN = "TEST-API-TOKEN-MOMA-836089C22111"
BASE = "https://api.momaapi.com"

本文数据用演示证书限量拉取(每接口返回近期样本),共 10 只样本股各 50 条日 K,合计 500 行 × 11 列。付费证书可拉取全量历史,量级到百万行时结论会更明显(见第四节)。

二、三种方案的真实基准

先别急着选方案,拿数据说话。我准备了 10 只股票各 50 条日 K(共 500 行),分别用 CSV、SQLite 写入磁盘,再分别测"全量读回内存"和"按 code 过滤"两个高频操作的耗时。所有数字均为本机实测。

python 复制代码
import time, sqlite3, os, csv
import requests

TOKEN = "TEST-API-TOKEN-MOMA-836089C22111"
BASE = "https://api.momaapi.com"

# 拉样本(演示证书,最小化代码,无重试------存储演示用)
r = requests.get(f"{BASE}/hslt/list/{TOKEN}", timeout=30)
stock_list = r.json()[:10]
rows_all = []
for s in stock_list:
    url = f"{BASE}/hsstock/history/{s['dm']}/d/n/{TOKEN}?st=20240101&et=20260630"
    for x in requests.get(url, timeout=40).json():
        x["code"] = s["dm"]; x["name"] = s["mc"]; rows_all.append(x)
print(f"样本: {len(rows_all)} 行 x {len(rows_all[0])} 列")

真实输出:

text 复制代码
样本: 500 行 x 11 列

2.1 写入耗时

python 复制代码
CSV, SQL = "kline.csv", "kline.sqlite"

def save_csv(rows):
    t0 = time.time()
    with open(CSV, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        w.writeheader(); w.writerows(rows)
    return (time.time() - t0) * 1000

def save_sqlite(rows):
    t0 = time.time()
    conn = sqlite3.connect(SQL)
    cols = list(rows[0].keys())
    conn.execute(f"CREATE TABLE IF NOT EXISTS kline({','.join(cols)})")
    q = ",".join("?" * len(cols))
    conn.executemany(f"INSERT INTO kline VALUES ({q})", [tuple(r.values()) for r in rows])
    conn.commit(); conn.close()
    return (time.time() - t0) * 1000

真实输出(本机实测):

text 复制代码
  CSV 写入:    19.6 ms   文件 40.6 KB
  SQLite 写入: 132.9 ms  文件 56.0 KB

小数据量下 CSV 反而更快------因为 SQLite 要建表、开事务,有固定开销。这个开销是一次性摊销的:当数据量从 500 行涨到 500 万行,CSV 的纯文本解析成本会线性放大,而 SQLite 的优势会逐步显现(见第四节扩展建议)。

2.2 全量读回内存

python 复制代码
def load_csv():
    t0 = time.time()
    with open(CSV, encoding="utf-8-sig") as f:
        list(csv.DictReader(f))
    return (time.time() - t0) * 1000

def load_sqlite():
    t0 = time.time()
    conn = sqlite3.connect(SQL); list(conn.execute("SELECT * FROM kline")); conn.close()
    return (time.time() - t0) * 1000

真实输出:

text 复制代码
  CSV 全量读:    47.9 ms
  SQLite 全量读: 127.2 ms

差距不大------500 行的数据量(40 KB)太小,磁盘 IO 主导,SQLite 的索引优势还没发挥。

2.3 条件查询(最常被忽视的差距)

python 复制代码
# CSV 方案:只能把全表读进内存再用 Python 过滤
with open(CSV, encoding="utf-8-sig") as f:
    hits = [x for x in csv.DictReader(f) if x["code"] == "000001.SZ"]

# SQLite 方案:库内过滤,只返回匹配行
conn = sqlite3.connect(SQL)
rows = list(conn.execute("SELECT * FROM kline WHERE code=?", ("000001.SZ",)))
conn.close()

真实输出:

text 复制代码
  CSV 读全表再过滤:  56.1 ms(含 IO,内存过滤)
  SQLite 索引过滤:   49.2 ms(库内过滤)

在本机 500 行样本下两者差距很小(约 1.1 倍)。但注意一个本质区别:CSV 必须每次把全表读进内存再过滤,而 SQLite 只在磁盘上检索匹配行。当表单边到几百万行,CSV 方案的内存占用会线性爆炸,SQLite 仍能稳定返回------这是工程化项目里 SQLite 的核心价值。

三、选型决策树:你的项目该选谁

场景 推荐 理由
临时分析 / 单次回测(<1 万行) CSV 零依赖、Excel 可直接打开、人工检查方便
单机小项目(10 万 ~ 100 万行) SQLite 【推荐】 单文件、零配置、并发安全、查询快
多人协作 / 数据量 > 1 亿行 MySQL / PostgreSQL 真正的服务进程,多用户权限与备份机制
多端同步 / Web 后端读取 MySQL 跨进程访问,SQLite 写锁会成瓶颈
回测历史归档 CSV + Parquet Parquet 列存压缩,1 亿行可压到 1 GB 以内

经验阈值:

  • 数据量 < 1 GB → SQLite 完全够用
  • 数据量 1~50 GB → MySQL 单机能扛
  • 数据量 > 50 GB → MySQL 分库分表 + Parquet 归档

四、SQLite 的 4 个工程化技巧

SQLite 是单文件 DB,看起来"玩具",但合理使用完全可以扛住个人量化项目。几个关键技巧:

4.1 启用索引(让条件查询真正快起来)

sql 复制代码
CREATE INDEX idx_code ON kline(code);
CREATE UNIQUE INDEX uniq_code_dt ON kline(code, trade_dt);

UNIQUE(code, trade_dt)增量更新的基础------同一天不会写两行。

4.2 增量更新(不重复写)

配合上面的唯一索引,写之前先 INSERT OR IGNORE,已存在的行自动跳过:

python 复制代码
conn = sqlite3.connect(SQL)
conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS uniq_code_dt ON kline(code, trade_dt)")
q = ",".join("?" * len(cols))
conn.executemany(f"INSERT OR IGNORE INTO kline VALUES ({q})", new_rows)
conn.commit(); conn.close()

4.3 编码与并发

  • 编码 :CSV 写盘用 encoding="utf-8-sig"(带 BOM),Excel 打开中文不乱码;读盘也用 utf-8-sig
  • 并发 :SQLite 写是库级锁,多进程同时写会 database is locked。解决:单写多读、或改用 MySQL。CSV 同理,多进程写同一文件要自己加锁。

4.4 备份策略

  • CSV:直接复制文件即可,但大文件复制慢。
  • SQLite:可用 VACUUM INTO 'backup.db' 在线热备,不阻塞读。
  • MySQL:用 mysqldump 或主从复制。

五、从 CSV 平滑迁移到 MySQL(最小 DDL)

当项目长大到需要 MySQL,SQLite 的表结构几乎可以直接平移。最小 DDL 模板(含主键/唯一索引):

sql 复制代码
CREATE TABLE kline (
  t    DATE,
  o    DECIMAL(10,3),
  h    DECIMAL(10,3),
  l    DECIMAL(10,3),
  c    DECIMAL(10,3),
  v    BIGINT,
  a    BIGINT,
  pc   DECIMAL(10,3),
  code VARCHAR(16),
  name VARCHAR(64),
  PRIMARY KEY (code, t),
  UNIQUE KEY uk_code_t (code, t)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

迁移只需把 executemany 的 SQLite 连接换成 MySQL 连接(如 pymysql),字段一一对应即可。

六、小结

存储方案没有"最好",只有"最合适"。本机实测的结论很朴素:小数据量下 CSV 零开销最省事;量级上到百万行,SQLite 的库内过滤和增量更新会变成刚需;再往上才是 MySQL 的舞台。 先用 CSV 把流程跑通,等数据真把你卡住了再升级------这是量化工程里最省心的节奏。

注意:本文为技术分享,所有接口调用均使用官方演示证书返回的演示数据(需替换为自己的实际证书) 魔码证书申请

相关推荐
名字还没想好☜1 小时前
Java 用 LinkedHashMap 三行实现 LRU 缓存:accessOrder、removeEldestEntry 与线程安全
java·后端·安全·spring·缓存
在所不辞兄1 小时前
【零基础学智能仿真-11】CatBoost——让材料类型和边界条件参与力学预测
人工智能·python·深度学习·神经网络·机器学习·工程技术
谢亮_vipxieliang1 小时前
ValidX vs Google Guava Preconditions:验证 vs 断言
java·spring boot·后端·spring cloud·hibernate·guava
2601_962097691 小时前
python的多线程编程之锁
python·多线程·同步·事件·
吴声子夜歌1 小时前
ApacheCommons——commons-configuration2(多数据源配置统一管理与热加载)
java·开发语言·算法·apache
星星落进兜里2 小时前
Java虚拟机面试题-补充
java·开发语言
2 小时前
Python 图片处理:裁剪缩放至300×200,格式JPG/PNG/GIF,控制大小≤100KB
linux·前端·python
2601_962294612 小时前
Python工具软件开发:借助InsCode AI IDE开启智能编程新时代
ide·python·ai·开发工具·智能化编程
2601_962097363 小时前
GraphQL,Grafana和Dash
python·grafana·数据可视化·graphql·dash