2. 数据源接入:CTP接口、易盛接口、以及第三方数据商(如万得、聚宽)的数据格式解析
做高频做市,第一步就是搞定数据源。这步要是没走稳,后面所有策略都是空中楼阁。
我见过不少团队,策略逻辑写得漂漂亮亮,结果一上线就崩——查到最后,是数据解析那层出了bug。说白了,数据接入是地基,地基不牢,地动山摇。
今天咱们就聊聊三种主流数据源:CTP、易盛、还有第三方数据商。我会结合我自己的踩坑经历,把每种接口的格式、解析要点、还有那些容易翻车的地方,都给你捋一遍。
2.1 CTP接口:国内期货的标配
CTP(综合交易平台)是上期技术开发的,国内绝大多数期货公司都用它。做高频做市,CTP几乎是绕不开的。
2.1.1 数据格式长什么样
CTP的行情数据,核心结构体是 CThostFtdcDepthMarketDataField。里面字段很多,但咱们做高频做市,重点关注这几个:
| 字段名 | 含义 | 我常用的处理方式 |
|---|---|---|
| InstrumentID | 合约代码 | 字符串,注意大小写 |
| LastPrice | 最新价 | 浮点数,精度到小数点后两位 |
| BidPrice1 ~ BidPrice5 | 买一到买五价格 | 数组,用于构建订单簿 |
| AskPrice1 ~ AskPrice5 | 卖一到卖五价格 | 同上 |
| BidVolume1 ~ BidVolume5 | 买一到买五量 | 整数,注意单位是手 |
| AskVolume1 ~ AskVolume5 | 卖一到卖五量 | 同上 |
| UpdateTime | 行情时间 | 字符串,需要转成时间戳 |
| UpdateMillisec | 毫秒 | 整数,和UpdateTime拼起来用 |
重点提醒:CTP的行情时间精度只到毫秒。对于高频做市来说,这其实有点不够用。我建议你在接收时,用本地时间戳做补充,尤其是做Tick级回测的时候。
2.1.2 解析代码示例
我用Python比较多,CTP的Python封装一般用 vnpy 或者 ctp-python。下面是我自己项目里用的解析片段:
class CTPDataParser:
def __init__(self):
self.order_book = {}
def parse_market_data(self, data):
"""解析CTP行情数据"""
tick = {
'symbol': data.InstrumentID,
'last_price': data.LastPrice,
'bid_prices': [data.BidPrice1, data.BidPrice2, data.BidPrice3],
'bid_volumes': [data.BidVolume1, data.BidVolume2, data.BidVolume3],
'ask_prices': [data.AskPrice1, data.AskPrice2, data.AskPrice3],
'ask_volumes': [data.AskVolume1, data.AskVolume2, data.AskVolume3],
'timestamp': f"{data.UpdateTime}.{data.UpdateMillisec:03d}"
}
return tick
我的小技巧:CTP的行情推送频率是500毫秒一次。如果你需要更细粒度的数据,可以考虑用FPGA或者DMA网卡来抓取交易所的原始组播数据。当然,这成本就上去了。
2.2 易盛接口:商品期权的利器
易盛(Esunny)在商品期权和部分商品期货上用得比较多。它的数据格式和CTP有些不同,但核心逻辑类似。
2.2.1 数据格式特点
易盛的行情数据,核心结构体是 ESAPIDepthMarketData。和CTP最大的区别在于:
- 深度支持更多档位:CTP只给5档,易盛可以给到10档甚至更多
- 时间精度更高:易盛支持微秒级时间戳
- 期权特有字段:比如隐含波动率、希腊字母等
我记得有一次做豆粕期权的做市策略,CTP拿不到足够的深度数据,换成易盛接口后,订单簿的厚度明显不一样了。
2.2.2 解析要点
易盛的API是C++写的,Python封装相对少一些。我一般用 ctypes 直接调用动态库:
import ctypes
class ESAPIDepthMarketData(ctypes.Structure):
_fields_ = [
("InstrumentID", ctypes.c_char * 31),
("UpdateTime", ctypes.c_char * 9),
("UpdateMillisec", ctypes.c_int),
("BidPrice", ctypes.c_double * 10),
("BidVolume", ctypes.c_int * 10),
("AskPrice", ctypes.c_double * 10),
("AskVolume", ctypes.c_int * 10),
("LastPrice", ctypes.c_double),
("OpenInterest", ctypes.c_double),
]
注意:易盛的行情数据是组播方式推送的,网络环境要求比较高。我曾经在机房调试时,因为交换机配置问题,丢包率高达5%——那数据根本没法用。建议用专门的组播接收卡,或者至少做好丢包重传机制。
2.3 第三方数据商:万得、聚宽
除了直接接交易所接口,很多团队也会用第三方数据商。原因很简单:省事。
2.3.1 万得(Wind)
万得的数据覆盖面广,从期货到股票到债券都有。但它的数据格式偏「金融分析」风格,不太适合高频场景。
万得的数据一般通过 WindPy 接口获取:
from WindPy import w
w.start()
# 获取期货行情
data = w.wsq("rb2401.SHF", "rt_last,rt_bid1,rt_ask1,rt_vol")
print(data.Data)
说实话,万得的行情数据延迟比较高,大概在几百毫秒到几秒不等。做回测分析可以,做实时做市就不太行了。
2.3.2 聚宽(JoinQuant)
聚宽的数据格式更贴近量化交易场景。它的Tick数据长这样:
{
'symbol': 'rb2401',
'current': 3850.0,
'bid': [3849.0, 3848.0, 3847.0],
'ask': [3851.0, 3852.0, 3853.0],
'bid_vol': [100, 200, 150],
'ask_vol': [80, 120, 90],
'time': '2024-01-15 14:30:00.123'
}
聚宽的数据格式比较规整,解析起来很舒服。但要注意,聚宽的数据是经过清洗的,和原始交易所数据可能会有细微差异。
我的建议:如果你做的是高频做市策略,尽量用CTP或易盛的原生接口。第三方数据商适合做策略回测和辅助分析。我自己的做法是:实盘用CTP,回测用聚宽,两边数据做交叉验证。
2.4 数据格式对比总结
为了让你看得更清楚,我画了一张对比图:
2.5 数据清洗的通用要点
不管用哪种数据源,有几个清洗要点是通用的:
- 时间对齐:不同数据源的时间格式不一样,统一转成时间戳
- 去重:CTP偶尔会推送重复的Tick,需要根据时间戳去重
- 异常值过滤:比如价格突然跳空、买卖价差为负等
- 缺失值处理:行情中断时的数据填充策略
避坑指南:我曾经在CTP数据里发现过「负的成交量」——这明显是数据异常。后来排查发现,是行情服务器重启后,某些字段被重置了。所以,一定要加一层数据校验逻辑,别盲目相信原始数据。
好了,数据源接入这块就聊到这儿。每种接口都有自己的脾气,摸透了就好办了。下一节咱们聊聊数据清洗的具体操作,到时候我会拿真实数据来演示。
公众号:蓝海资料掘金营,微信deep3321