数据采集与存储:API接入、WebSocket实时流、CSV/Parquet文件存储、数据库选型

做量化交易,数据就是你的弹药库。弹药质量不行,再好的策略也白搭。这一章我重点聊聊数据怎么来、怎么存、怎么选型。说白了,就是要把行情数据从交易所搬到你的系统里,还要保证它不丢、不乱、能快速查。

核心原则:数据采集要「稳」,存储要「快」,恢复要「准」。这三条缺一不可。

API接入:REST vs WebSocket,怎么选?

我个人习惯把API接入分成两类:拉模式(REST)和推模式(WebSocket)。

REST API 适合做历史数据补全和定时快照。比如每天收盘后拉一次日线数据,或者每隔5秒拉一次最新价。优点是简单、稳定,不容易断。缺点也很明显——有延迟,而且频繁请求容易被限流。

WebSocket 则是实时行情的标配。交易所一有成交,服务器就主动推给你。延迟通常在毫秒级别。我在项目中遇到过一个问题:WebSocket连接偶尔会断开,而且断开后不会自动重连。嗯,这里要注意,必须自己实现心跳检测和重连机制。

我的建议:REST + WebSocket 双通道。WebSocket 负责实时流,REST 负责补数据和做校验。这样即使 WebSocket 断了几秒,也能用 REST 拉回来。

// 伪代码:WebSocket 重连逻辑
function connectWebSocket() {
    ws = new WebSocket(url);
    ws.onOpen = () => { 
        heartbeatInterval = setInterval(sendPing, 30000); 
    };
    ws.onClose = () => {
        clearInterval(heartbeatInterval);
        setTimeout(connectWebSocket, 1000); // 1秒后重连
    };
    ws.onMessage = (data) => {
        // 校验数据完整性
        if (isValid(data)) {
            processData(data);
        }
    };
}

WebSocket实时流:数据校验是第一道防线

实时流的数据量很大,每秒可能上千条。如果每条都做完整校验,CPU扛不住。我的做法是分层校验:

  • 第一层:格式校验——JSON能不能解析?字段齐不齐?
  • 第二层:逻辑校验——时间戳是不是递增的?价格是不是正数?
  • 第三层:一致性校验——和REST API拉到的快照对比,偏差超过阈值就报警

我曾经踩过一个坑:某交易所的WebSocket推送的成交价偶尔会多一个零。比如实际价格是5000,它推成50000。如果直接入库,策略就全乱了。所以我现在都会加一个「价格突变检测」——如果当前价格和前一笔相差超过5%,就标记为可疑数据,等REST确认后再处理。

注意:WebSocket数据不要直接写入数据库。先写入内存队列(比如RingBuffer),由另一个线程批量写入。这样可以避免高频写入导致数据库锁竞争。

CSV/Parquet文件存储:离线分析的基石

实时数据存数据库,历史数据存文件。这是我的铁律。

CSV 的好处是通用,Excel都能打开。但缺点也很明显:没有压缩,没有索引,大文件读写慢。我一般只用CSV做数据交换,比如给同事发一份日线数据。

Parquet 才是正经的列式存储格式。压缩率高(能压到CSV的1/5),读取快(只读需要的列),还支持谓词下推。我建议所有历史行情数据都用Parquet存。

# Python 示例:写入 Parquet
import pandas as pd
import pyarrow.parquet as pq

df = pd.DataFrame({
    'timestamp': [1625097600, 1625097601],
    'symbol': ['BTCUSDT', 'BTCUSDT'],
    'price': [35000.5, 35001.2],
    'volume': [0.1, 0.2]
})

# 按日期分区存储
df.to_parquet('data/2025/01/15/btc_trades.parquet', 
              compression='snappy')

小技巧:Parquet文件按日期分区,每天一个文件夹。查询时只扫需要的日期,速度能快10倍以上。

数据库选型:InfluxDB vs ClickHouse

这是大家问得最多的问题。我直接说结论:

特性 InfluxDB ClickHouse
写入速度 极快(专门为时序优化) 快(批量写入更优)
查询灵活性 一般(SQL-like,但有限制) 强(完整SQL支持)
压缩率 中等 高(列式存储)
运维复杂度 低(单机就能跑) 中等(集群配置稍复杂)
适合场景 实时监控、Tick级数据 历史分析、多表关联查询

我的选择逻辑:

  • 如果只存行情数据,不做复杂分析 → InfluxDB。部署简单,写入快,查询也够用。
  • 如果既要存行情,又要做因子计算、回测分析 → ClickHouse。SQL灵活,能直接跑聚合查询。

我曾经在一个项目里两个都用过:InfluxDB 存实时Tick,ClickHouse 存分钟级聚合数据。实时看盘用 InfluxDB,回测分析用 ClickHouse。各取所长,互不干扰。

避坑指南:InfluxDB 的 tag 字段不要太多。我见过有人把订单ID也设成tag,结果索引爆炸,写入性能直接掉到原来的1/10。记住:tag 用于过滤,field 用于存储。能放 field 的别放 tag。

数据校验与错误恢复:实战中的血泪教训

数据采集做得再好,也难免出问题。网络抖动、交易所宕机、磁盘写满……什么情况都可能发生。我的恢复策略分三步:

  1. 本地缓存——WebSocket 数据先写本地文件(按分钟滚动),再异步写入数据库。即使数据库挂了,数据也不会丢。
  2. 数据对账——每天凌晨跑一次对账任务,用 REST API 拉取交易所的历史数据,和本地数据库做对比。发现缺失就补录。
  3. 异常标记——校验不通过的数据不要直接丢弃,而是标记为「可疑」,存入单独的异常表。人工审核后再决定是修正还是删除。
-- ClickHouse 示例:创建异常数据表
CREATE TABLE trade_anomalies (
    timestamp DateTime,
    symbol String,
    price Float64,
    volume Float64,
    anomaly_reason String,
    raw_data String
) ENGINE = MergeTree()
ORDER BY timestamp;

嗯,这套机制帮我避免过好几次灾难。有一次交易所的WebSocket推送了半小时的乱序数据,因为我有本地缓存和对账机制,最终只丢了3秒的数据,而且很快补回来了。

总结一句话:数据采集要双通道,存储要分层,校验要前置,恢复要自动。做到这四点,你的行情数据系统就稳了。

行情数据采集与存储架构 交易所 REST API 历史数据 / 补全 交易所 WebSocket 实时行情推送 数据采集层 • 格式校验 • 逻辑校验 • 一致性校验 • 异常标记 • 本地缓存 存储层 InfluxDB ClickHouse Parquet 文件 CSV 文件 异常数据表 本地文件缓存 按分钟滚动写入 每日对账任务 REST 拉取 vs 本地数据 图例: 数据源 采集层 存储层 缓存 对账 异步写入 数据流方向:交易所 → 采集校验 → 存储/缓存 → 对账补全
专注资料整理