第19章:数据库存储与查询——量化交易的数据底座
做量化交易,说白了就是跟数据打交道。基差数据、持仓数据、历史K线、实时行情……这些数据往哪儿放?怎么存?怎么查?我见过不少团队,策略模型写得漂亮,结果卡在数据存取上——查询慢、容易丢、备份混乱。今天咱们就把这块彻底聊透。
核心观点:没有完美的数据库,只有适合场景的选择。量化交易的数据存储,要同时考虑写入速度、查询效率、数据完整性和运维成本。
19.1 SQLite vs PostgreSQL:怎么选?
这两个是我最常用的关系型数据库。很多人纠结选哪个,其实没那么复杂。
| 对比维度 | SQLite | PostgreSQL |
|---|---|---|
| 部署方式 | 嵌入式,单文件 | 客户端-服务器架构 |
| 并发写入 | 只支持单写 | 多写并发,MVCC |
| 数据量上限 | 约140TB(理论) | 几乎无上限 |
| 查询功能 | 基础SQL | 窗口函数、CTE、全文检索 |
| 适合场景 | 个人回测、小团队原型 | 生产环境、多用户协作 |
我个人习惯:本地回测用SQLite,部署到服务器上跑实盘用PostgreSQL。为什么?SQLite零配置,一个文件搞定,适合快速验证。但如果你同时跑多个策略,或者需要多人共享数据,PostgreSQL的并发能力就体现出来了。
小技巧:我曾在项目中用SQLite存了3年的1分钟K线数据,大概2亿条。查询时加了索引,速度还行。但一旦开始做跨品种关联查询,明显感觉吃力。后来迁移到PostgreSQL,同样的查询快了10倍不止。
19.2 数据表设计——基差交易的核心表结构
设计表结构,我踩过不少坑。最惨的一次,表设计不合理,导致回测数据要重跑三天……嗯,从那以后我特别重视这个环节。
以基差交易为例,核心数据表至少需要这几张:
19.2.1 合约信息表(contract_info)
CREATE TABLE contract_info (
contract_id TEXT PRIMARY KEY, -- 合约代码,如 'rb2401'
symbol TEXT NOT NULL, -- 品种,如 'RB'
exchange TEXT, -- 交易所
list_date DATE, -- 上市日期
expire_date DATE, -- 到期日期
multiplier INTEGER, -- 合约乘数
tick_size NUMERIC(6,4) -- 最小变动价位
);
19.2.2 基差数据表(basis_data)
CREATE TABLE basis_data (
id SERIAL PRIMARY KEY,
trade_date DATE NOT NULL,
contract_id TEXT REFERENCES contract_info(contract_id),
spot_price NUMERIC(12,2), -- 现货价格
futures_price NUMERIC(12,2), -- 期货价格
basis NUMERIC(12,2), -- 基差 = 现货 - 期货
basis_ratio NUMERIC(8,4), -- 基差率
volume INTEGER, -- 成交量
open_interest INTEGER, -- 持仓量
UNIQUE(trade_date, contract_id)
);
注意:基差表一定要加唯一约束。我曾经因为忘记加,导致同一日期同一合约插入了两条记录,回测结果完全对不上。排查了整整一天……
19.2.3 交易信号表(trade_signals)
CREATE TABLE trade_signals (
signal_id SERIAL PRIMARY KEY,
trade_date DATE NOT NULL,
strategy_name TEXT,
contract_pair TEXT, -- 如 'RB-HC' 螺纹钢-热卷
signal_type TEXT, -- 'enter_long', 'enter_short', 'exit'
entry_price NUMERIC(12,2),
stop_loss NUMERIC(12,2),
target_price NUMERIC(12,2),
confidence NUMERIC(4,2) -- 信号置信度
);
19.3 时间序列数据库——InfluxDB
做量化的人都知道,行情数据本质上是时间序列。关系型数据库存时间序列,说实话有点「用牛刀杀鸡」的感觉。InfluxDB就是专门干这个的。
为什么选InfluxDB?
- 写入速度极快——实测单机每秒能写几十万点
- 数据压缩率高——同样的数据,比PostgreSQL省70%空间
- 自带降采样——自动聚合历史数据,查询秒级响应
- 类SQL查询——学习成本低
举个例子,存储1分钟K线数据:
-- 创建保留策略,数据保留90天
CREATE RETENTION POLICY "one_month" ON "market_data" DURATION 90d REPLICATION 1 DEFAULT
-- 写入数据(通过Python客户端)
from influxdb import InfluxDBClient
client = InfluxDBClient(host='localhost', port=8086)
json_body = [
{
"measurement": "kline_1min",
"tags": {
"symbol": "RB2401",
"exchange": "SHFE"
},
"time": "2024-01-15T09:30:00Z",
"fields": {
"open": 3980.0,
"high": 3995.0,
"low": 3975.0,
"close": 3990.0,
"volume": 12500
}
}
]
client.write_points(json_body)
我的经验:InfluxDB的tag字段会被索引,所以把symbol、exchange这类经常查询的字段设为tag。field字段不会被索引,适合存数值。这个设计理念跟关系型数据库正好相反,刚开始用的时候容易搞混。
19.4 数据备份策略——别等丢了才后悔
做量化最怕什么?策略亏钱?不,是数据丢了。策略亏了还能优化,数据丢了就真的什么都没了。我有个朋友,硬盘坏了,三年的Tick数据全没了……从那以后,我养成了「备份强迫症」。
我的备份策略:
- 本地备份:每天凌晨自动执行pg_dump,保留最近7天
- 异地备份:每周同步到云存储(阿里云OSS或AWS S3)
- 增量备份:对于InfluxDB,使用连续查询做降采样备份
- 冷备份:每月一次全量备份,存到移动硬盘
PostgreSQL备份脚本示例:
#!/bin/bash
# 每天凌晨2点执行
BACKUP_DIR="/data/backup/postgres"
DB_NAME="quant_trading"
DATE=$(date +%Y%m%d)
pg_dump -U quant_user -h localhost $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
重要提醒:备份一定要验证!我见过太多人只备份不验证,等真要用的时候发现备份文件损坏。建议每周手动恢复一次备份到测试环境,确保数据可用。
19.5 本章知识体系
下面这张图,把本章的核心逻辑串起来了。你想想看,从数据产生到存储、查询、备份,每一步都有对应的技术选型。
这张图展示了数据从产生到存储、查询、备份的完整链路。说白了,每个环节选对工具,你的量化系统才能跑得稳、查得快、丢不了。
公众号:蓝海资料掘金营,微信deep3321