第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数据全没了……从那以后,我养成了「备份强迫症」。

我的备份策略:

  1. 本地备份:每天凌晨自动执行pg_dump,保留最近7天
  2. 异地备份:每周同步到云存储(阿里云OSS或AWS S3)
  3. 增量备份:对于InfluxDB,使用连续查询做降采样备份
  4. 冷备份:每月一次全量备份,存到移动硬盘

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 本章知识体系

下面这张图,把本章的核心逻辑串起来了。你想想看,从数据产生到存储、查询、备份,每一步都有对应的技术选型。

量化交易数据存储体系 数据源 行情数据 · 基差数据 · 交易信号 存储层 SQLite 个人回测 · 小数据量 PostgreSQL 生产环境 · 多用户 InfluxDB 时间序列 · 高频数据 查询层 SQL查询 · 时间序列聚合 · 跨品种关联 · 信号生成 备份策略 本地备份 · 异地备份 · 增量备份 · 冷备份

这张图展示了数据从产生到存储、查询、备份的完整链路。说白了,每个环节选对工具,你的量化系统才能跑得稳、查得快、丢不了。


公众号:蓝海资料掘金营,微信deep3321