4. 数据格式规范:CSV、Parquet、HDF5、数据库表结构设计原则

在量化回测实验中,数据格式的选择直接影响存储效率、读写速度以及后续分析的便捷性。本章将系统梳理四种主流数据格式(CSV、Parquet、HDF5、数据库表)的适用场景、核心差异与设计原则,帮助你在不同实验阶段做出合理的技术选型。

4.1 四种格式的核心特性对比

下表从存储效率、读写性能、类型支持、元数据管理四个维度进行横向对比,为后续的格式选择提供决策依据。

特性维度 CSV Parquet HDF5 数据库表
存储效率 低(纯文本,无压缩) 高(列式压缩,支持多种编码) 中高(支持压缩与分块) 中(依赖存储引擎)
读写性能 写快读慢(全表扫描) 读快写中(列裁剪、谓词下推) 读快写快(分块I/O) 读快写快(索引优化)
类型支持 弱(仅字符串,需后处理) 强(原生支持时间、嵌套结构) 强(支持复合数据类型) 强(严格模式约束)
元数据管理 无(需外部维护) 内嵌(Schema与统计信息) 内嵌(Group/Dataset结构) 系统级(Catalog与字典)
适用阶段 原始数据采集、快速预览 大规模分析、特征工程 科学计算、多维数组存储 生产环境、多用户并发
选型建议: 在回测实验的早期探索阶段,优先使用 CSV 进行快速验证;当数据量超过 1GB 或需要频繁读取特定列时,立即迁移至 Parquet;若涉及多维时间序列或矩阵运算,HDF5 是更优选择;生产级回测系统必须使用数据库表来保证事务一致性与并发控制。

4.2 CSV 格式规范与陷阱

CSV 是量化回测中最通用的交换格式,但其缺乏类型约束的特性容易引入数据质量问题。以下是经过实践检验的 CSV 规范清单:

  • 列名规范: 使用小写字母 + 下划线,避免空格与特殊字符。例如 close_price 而非 Close Price。
  • 时间列格式: 统一使用 ISO 8601 标准(YYYY-MM-DD HH:mm:ss),避免歧义。
  • 缺失值处理: 使用空字符串或 NaN,禁止使用 N/A、null 等混合标记。
  • 浮点数精度: 价格字段保留 4 位小数,成交量字段保留 2 位小数,避免无意义的长尾数字。
  • 文件编码: 强制使用 UTF-8 without BOM,避免跨平台乱码。
# 符合规范的 CSV 示例
timestamp,symbol,open,high,low,close,volume
2024-01-02 09:30:00,AAPL,185.5000,186.2000,184.8000,185.9000,1250000.00
2024-01-02 09:31:00,AAPL,185.9000,186.5000,185.7000,186.1000,980000.00
常见陷阱: 不要依赖 CSV 的隐式类型转换。例如,volume 列如果包含逗号分隔符(如 1,250,000),会被解析为字符串而非数值。始终在读取时显式指定数据类型。

4.3 Parquet 列式存储设计

Parquet 是当前大数据生态中回测数据存储的事实标准。其列式存储结构允许只读取需要的列,大幅减少 I/O 开销。设计 Parquet 数据集时需关注以下原则:

  • 分区策略: 按日期或 symbol 进行分区,避免单文件过大。推荐分区粒度:日频数据按 year/month/day 分区,高频数据按 year/month/day/hour 分区。
  • 压缩算法: 优先使用 snappy(平衡压缩比与解压速度)或 zstd(更高压缩比)。避免使用 gzip 在回测场景中造成性能瓶颈。
  • Schema 演进: 允许新增列时使用 NULL 默认值,避免破坏已有数据文件。
  • 行组大小: 设置为 512MB ~ 1GB,兼顾内存占用与并行读取效率。
性能基准: 在相同硬件条件下,读取 100 列中的 5 列时,Parquet 比 CSV 快 10~20 倍,磁盘占用减少 60%~80%。对于包含 3 年 tick 级数据的回测库,建议全量迁移至 Parquet。
Parquet 列式存储 vs CSV 行式存储:读取 5 列时的 I/O 差异 CSV(行式) 行 1: 全部 100 列 行 2: 全部 100 列 行 3: 全部 100 列 ... 行 N: 全部 100 列 必须读取全部 100 列 I/O 开销:100% 对比 Parquet(列式) 列 A: 全部行 列 B: 全部行 列 C: 全部行 列 D: 全部行 列 E: 全部行 列 F~Z: 跳过 (不读取) 仅读取需要的 5 列 I/O 开销:5% 结论:Parquet 列裁剪特性使回测中仅读取所需字段,I/O 效率提升 10~20 倍,尤其适合包含数百列的高频数据。 CSV 必须全行扫描,即使只需要 1 列也要加载全部数据,造成严重的资源浪费。

4.4 HDF5 多维数据存储

HDF5 在量化回测中主要用于存储多维时间序列数据(如期权隐含波动率曲面、因子暴露矩阵)和模型参数快照。其核心设计原则包括:

  • Group 组织: 使用 /market/equity/、/factors/alpha/ 等层级结构管理数据集,避免扁平化命名。
  • Chunk 设置: 根据访问模式设置分块大小。按时间序列读取时,设置 chunks=(10000, 1);按截面读取时,设置 chunks=(1, 500)。
  • 压缩与缓存: 启用 complib='blosc:lz4' 获得最佳读写平衡;设置 chunk_cache_mem_size 为 1GB 以上以加速重复访问。
  • 版本管理: 在 Group 属性中记录数据生成时间、回测版本号、参数哈希,便于实验追溯。
# HDF5 数据集创建示例(Python)
import h5py
import numpy as np

with h5py.File('vol_surface.h5', 'w') as f:
    # 创建 Group 层级
    grp = f.create_group('/options/SPX/2024')
    # 创建数据集:行=时间,列=行权价
    ds = grp.create_dataset(
        'implied_vol',
        shape=(10000, 50),
        dtype='f4',
        chunks=(1000, 50),
        compression='blosc:lz4',
        shuffle=True
    )
    # 写入元数据
    ds.attrs['generated_at'] = '2024-06-01T00:00:00'
    ds.attrs['model_version'] = 'v2.3.1'
    ds.attrs['param_hash'] = 'a1b2c3d4'
适用场景: 当你的回测需要频繁读取 3 维或 4 维数据(如时间 × 标的 × 行权价 × 波动率)时,HDF5 的块存储和部分 I/O 能力远优于 Parquet。但注意 HDF5 不适合多进程并发写入,生产环境需加锁或使用只读模式。

4.5 数据库表结构设计原则

生产级回测系统通常使用关系型数据库(PostgreSQL、ClickHouse)或时序数据库(InfluxDB、TimescaleDB)来管理实验数据。以下是经过验证的表结构设计原则:

  • 范式化与反范式化平衡: 基础行情数据采用 3NF 减少冗余;回测结果宽表采用反范式化,避免频繁 JOIN 影响性能。
  • 分区与索引: 按时间范围分区(如按月分区),在 timestamp 和 symbol 上建立复合索引,覆盖 90% 以上的查询模式。
  • 数据类型精简化: 价格字段使用 DECIMAL(12,4) 而非 FLOAT,避免浮点误差;成交量使用 BIGINT;状态字段使用 SMALLINT 枚举。
  • 审计字段: 每张表必须包含 created_at、updated_at、batch_id 三个审计字段,支持数据回溯与问题排查。
-- 回测结果表结构示例(PostgreSQL)
CREATE TABLE backtest_results (
    result_id       BIGSERIAL PRIMARY KEY,
    strategy_id     INTEGER NOT NULL REFERENCES strategies(id),
    symbol          VARCHAR(10) NOT NULL,
    trade_date      DATE NOT NULL,
    entry_price     DECIMAL(12,4),
    exit_price      DECIMAL(12,4),
    position_size   BIGINT,
    pnl             DECIMAL(16,4),
    commission      DECIMAL(10,4),
    slippage        DECIMAL(10,4),
    status          SMALLINT DEFAULT 0,  -- 0:待结算, 1:已结算, 2:异常
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    batch_id        UUID NOT NULL
) PARTITION BY RANGE (trade_date);

-- 创建月度分区
CREATE TABLE backtest_results_202401 PARTITION OF backtest_results
    FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

-- 复合索引
CREATE INDEX idx_results_date_symbol ON backtest_results (trade_date, symbol);
数据库表结构设计原则(生产级回测系统) 表结构设计原则 范式化平衡 3NF + 反范式宽表 分区与索引 时间分区 + 复合索引 类型精简化 DECIMAL / BIGINT 审计字段 created_at / batch_id 实践要点 • 基础行情表:3NF 设计,减少存储冗余,便于数据一致性维护 • 回测结果表:反范式宽表,单表包含所有指标字段,避免回测分析时多表 JOIN • 索引策略:覆盖 90% 查询模式(时间范围 + 标的代码),避免过度索引影响写入性能 • 审计字段:每张表强制包含,支持数据血缘追踪与实验回滚

4.6 格式选型决策流程

在实际项目中,数据格式的选择往往不是单一的,而是根据数据生命周期不同阶段采用混合策略。以下决策流程可帮助你快速定位:

  1. 数据采集阶段: 使用 CSV 作为原始数据落地格式,保留最原始的文本形态,便于人工校验与第三方工具导入。
  2. 数据清洗阶段: 转换为 Parquet,利用列式存储加速特征工程与缺失值处理。
  3. 回测计算阶段: 高频多维数据使用 HDF5,低频宽表数据使用 Parquet,结果数据写入数据库表。
  4. 结果分析阶段: 从数据库表或 Parquet 中读取聚合结果,使用 CSV 导出用于报告与可视化。
最佳实践: 在回测实验管理系统中,建议建立统一的 data_catalog 元数据表,记录每个数据集的格式、分区信息、生成时间与校验哈希。这样无论底层使用何种格式,上层分析代码都可以通过目录服务透明访问,实现存储格式与业务逻辑的解耦。