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。下图展示了一个典型的直连数据流架构:
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 存储格式选择决策流程
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仅用于调试。