2. Tick数据获取:常见数据源介绍与API调用实战

高频策略的起点是可靠、低延迟的Tick数据。本章将系统梳理Tick数据的获取渠道、API调用方法及存储选型,帮助你在实战中快速搭建数据管道。

2.1 常见数据源对比

Tick数据源主要分为交易所直连和第三方数据商两大类。下表从延迟、成本、数据完整性三个维度进行对比:

数据源类型 代表厂商/接口 延迟(典型值) 年成本(万元) 数据完整性 适用场景
交易所直连 CTP(上期技术)、XONE(中金所)、FIX/FAST < 1ms 10-50(含席位费) 最高,逐笔成交+逐笔委托 自营高频、做市商
第三方数据商 Wind、聚宽、RiceQuant、Tushare、QuantConnect 10-100ms 0.5-5(按API调用量) 较高,通常为切片快照 策略回测、中低频研究
开源/社区数据 Binance API、Bybit API、CCXT 100-500ms 免费(限频) 中等,依赖网络稳定性 加密货币、个人研究
选型建议:如果策略依赖逐笔委托流(Order Flow Imbalance),必须选择交易所直连或提供Level-2数据的第三方商;仅做日频或分钟级回测,第三方数据商完全够用。

2.2 交易所直连架构

交易所直连通常采用FIX/FAST协议或CTP API。下图展示了一个典型的直连数据流架构:

交易所直连Tick数据流架构 交易所撮合引擎 CTP / FIX / FAST 交易网关 行情解码 + 缓存 组播/单播 数据分发层 ZeroMQ / Kafka 实时存储 Parquet / HDF5 策略引擎 信号计算 + 下单 关键延迟点 • 网卡中断处理:< 5μs • 内核旁路(DPDK):< 1μs • 应用层解码:< 10μs

2.3 Tick数据API调用实战

以下以CTP API(C++封装)和Binance WebSocket(Python)为例,展示Tick数据的实时获取代码。

2.3.1 CTP行情API(C++)

// CTP行情订阅示例(简化版)
#include <ThostFtdcMdApi.h>

class CMdSpi : public CThostFtdcMdSpi {
public:
    // 深度行情回调
    virtual void OnRtnDepthMarketData(
        CThostFtdcDepthMarketDataField *pDepthMarketData) {
        printf("合约: %s, 最新价: %.2f, 成交量: %d, 时间: %s\n",
               pDepthMarketData->InstrumentID,
               pDepthMarketData->LastPrice,
               pDepthMarketData->Volume,
               pDepthMarketData->UpdateTime);
    }
};

int main() {
    CThostFtdcMdApi *pMdApi = CThostFtdcMdApi::CreateFtdcMdApi();
    CMdSpi *pMdSpi = new CMdSpi();
    pMdApi->RegisterSpi(pMdSpi);
    pMdApi->RegisterFront("tcp://180.168.146.187:10010");
    pMdApi->Init();
    // 订阅合约
    char *ppInstrumentID[] = {"rb2410", "cu2409"};
    pMdApi->SubscribeMarketData(ppInstrumentID, 2);
    // 进入事件循环
    pMdApi->Join();
    return 0;
}

2.3.2 Binance WebSocket(Python)

import asyncio
import json
import websockets

async def subscribe_tick(symbol="btcusdt"):
    url = "wss://stream.binance.com:9443/ws"
    subscribe_msg = {
        "method": "SUBSCRIBE",
        "params": [f"{symbol}@depth20@100ms"],
        "id": 1
    }
    async with websockets.connect(url) as ws:
        await ws.send(json.dumps(subscribe_msg))
        while True:
            data = await ws.recv()
            tick = json.loads(data)
            # 提取买卖盘口
            bids = tick.get('b', [])[:5]   # 前5档买单
            asks = tick.get('a', [])[:5]   # 前5档卖单
            print(f"时间: {tick.get('E')}, 买一: {bids[0]}, 卖一: {asks[0]}")

asyncio.run(subscribe_tick())
实战要点:
  • CTP API需要先登录(ReqUserLogin),行情订阅前必须完成认证。
  • WebSocket连接建议设置心跳(ping/pong),防止连接被防火墙断开。
  • 生产环境必须处理断线重连,建议使用指数退避策略。

2.4 数据存储格式选型

Tick数据量巨大(单品种每日可达数百万条),存储格式直接影响读写性能和磁盘占用。三种主流格式对比如下:

存储格式 压缩比 写入速度 读取速度(列式) 支持复杂类型 推荐场景
CSV 1:1(无压缩) 中等 慢(全表扫描) 否 小样本、调试、人工查看
Parquet 3:1 ~ 8:1 快(列式写入) 极快(谓词下推) 是(嵌套结构) 大规模回测、特征工程
HDF5 2:1 ~ 5:1 快(连续写入) 快(分块读取) 是(多维数组) 实时存储、科学计算

2.4.1 存储格式选择决策流程

Tick数据存储格式选择决策树 Tick数据存储需求 数据量 < 10GB CSV 快速查看 / 调试 数据量 ≥ 10GB 列式分析? 是(回测/特征) Parquet 列式压缩 + 谓词下推 否(实时写入) HDF5 连续写入 + 分块读取

2.4.2 Parquet写入示例(Python + Pandas)

import pandas as pd
import pyarrow.parquet as pq
import pyarrow as pa

# 模拟Tick数据
tick_data = pd.DataFrame({
    'timestamp': pd.date_range('2024-01-01', periods=1000000, freq='1ms'),
    'symbol': ['rb2410'] * 1000000,
    'price': np.random.uniform(3500, 3600, 1000000),
    'volume': np.random.randint(1, 100, 1000000),
    'side': np.random.choice(['buy', 'sell'], 1000000)
})

# 写入Parquet(启用Snappy压缩)
table = pa.Table.from_pandas(tick_data)
pq.write_table(table, 'tick_rb2410.parquet',
               compression='snappy',
               row_group_size=50000)

# 读取时只加载需要的列
df_read = pd.read_parquet('tick_rb2410.parquet',
                          columns=['timestamp', 'price', 'volume'])
print(f"读取行数: {len(df_read)}, 内存占用: {df_read.memory_usage().sum()/1e6:.2f} MB")
存储避坑:
  • CSV不适合长期存储Tick数据——单日文件可达数GB,且无schema校验。
  • HDF5在并发写入时存在锁竞争,多进程场景建议使用Parquet + 分区目录。
  • Parquet的row_group_size建议设为50000-100000,平衡压缩率和读取性能。

2.5 本章小结

Tick数据获取是高频策略的基石。核心要点:

  • 数据源选择:交易所直连适合低延迟场景,第三方数据商适合研究和回测。
  • API调用:CTP和WebSocket是两种主流方式,注意心跳和重连机制。
  • 存储选型:Parquet是回测和特征工程的首选,HDF5适合实时写入,CSV仅用于调试。