数据基础设施:数据源选择、清洗对齐与存储方案
做跨境做市,说白了就是跟数据打交道。你模型再牛,策略再精,数据源要是烂了,一切都是白搭。我见过太多团队,花大把时间调参数,结果问题出在数据上——交易所的行情延迟了、OTC的报价对不齐、新闻舆情根本没解析对。嗯,今天我们就来聊聊数据基础设施怎么搭。
数据源选择:交易所API、OTC报价、新闻舆情
数据源的选择,决定了你风控模型的上限。我个人习惯把数据源分成三类:核心行情、场外报价、另类数据。
交易所API
交易所API是主力。你想想看,做市的核心就是盯盘口、抓成交。我建议至少接入两家以上的交易所,防止单点故障。我在项目中遇到过,某家交易所的WebSocket突然断连,结果我们的报价直接卡了3秒——那3秒的损失,够买好几台服务器了。
常见的交易所API包括:
- REST API:用于获取历史数据、账户信息、下单。适合低频操作。
- WebSocket:实时推送行情、成交、订单簿变化。做市必备。
- FIX协议:机构级交易接口,延迟低,但对接成本高。
这里有个坑:不同交易所的数据格式差异很大。比如Binance的订单簿是bids和asks数组,而Coinbase的则是bids和asks对象。你写解析器的时候,一定要做统一抽象。
核心原则:数据源必须冗余。至少两个独立的数据源,才能交叉验证。
OTC报价
OTC报价是跨境做市的特色。交易所的流动性有时候不够深,大单得走OTC。OTC报价通常来自券商、做市商或者暗池。这些报价不是公开的,你得跟对手方签协议。
OTC数据的特点:
- 非标准化:每家报价格式都不一样。有的用CSV邮件,有的用FIX,有的甚至用Excel。
- 延迟高:OTC报价通常是T+1或者T+0.5,不像交易所那么实时。
- 信用风险:OTC交易对手可能违约,所以报价里得包含信用评级信息。
我曾经对接过一家日本券商的OTC报价,他们居然用PDF附件发报价单。我当时的表情——嗯,你懂的。后来我写了个PDF解析器,硬是把数据抠出来了。
新闻舆情
新闻舆情是另类数据。做市不只是看盘口,还得看宏观。比如美联储加息、地缘冲突、公司财报,这些都会影响汇率和价差。
新闻舆情的数据源包括:
- 新闻API:比如Reuters、Bloomberg、NewsAPI。注意,这些API有调用频率限制。
- 社交媒体:Twitter、Reddit。但噪音很大,需要做情感分析。
- 经济日历:非农数据、CPI、GDP发布的时间点。这些是确定性事件,可以提前做风控。
我个人习惯把新闻舆情做成事件驱动。比如,当美联储利率决议发布时,自动触发风控模型的压力测试。这样能提前锁定风险敞口。
数据清洗与对齐
数据源选好了,接下来就是脏活累活——数据清洗。说实话,80%的时间都花在这上面。我刚开始做的时候,以为模型是核心,后来发现数据清洗才是。
常见的数据问题
| 问题类型 | 描述 | 处理方法 |
|---|---|---|
| 缺失值 | 某段时间的行情数据为空 | 前向填充、插值、或者从备用数据源补全 |
| 异常值 | 价格突然跳变,比如从100跳到10000 | 用Z-score或IQR检测,然后剔除或修正 |
| 重复数据 | 同一笔成交被推送两次 | 用时间戳+交易ID去重 |
| 时间戳不一致 | 不同交易所的时间戳格式不同 | 统一转为UTC毫秒时间戳 |
举个例子。我在处理某交易所的订单簿数据时,发现它的timestamp字段居然是字符串格式的"2025-01-15T10:30:00Z",而另一家交易所是整数毫秒。你想想看,如果不统一,对齐的时候就会错位。我的做法是:所有时间戳统一转为int64的UTC毫秒。
数据对齐
数据对齐是跨境做市的难点。不同交易所的行情更新频率不一样,有的100ms推送一次,有的500ms。OTC报价可能每小时才更新一次。新闻舆情更是异步的。
对齐的核心思路是:以高频数据为基准,低频数据做插值或对齐到最近的时间窗口。
比如,你的风控模型需要每秒计算一次风险敞口。那么:
- 交易所行情:用最新的快照。
- OTC报价:如果当前时间没有新报价,就用上一笔。
- 新闻舆情:如果新闻事件发生在当前时间窗口内,就标记为1,否则为0。
小技巧:我习惯用pandas的reindex方法做时间对齐。先创建一个统一的时间索引,然后对每个数据源做asof合并。这样能保证每个时间点都有数据。
数据存储方案:时序数据库
数据清洗完,得存起来。做市数据的特点是:高频、海量、时间敏感。普通的关系型数据库根本扛不住。我建议用时序数据库。
为什么选时序数据库?
你想想看,做市数据每秒可能产生几千条行情记录。如果用MySQL,写入压力大,查询也慢。时序数据库专门为这种场景设计:
- 高写入吞吐:支持每秒百万级写入。
- 时间范围查询快:按时间戳索引,查询效率高。
- 数据压缩:时序数据有规律,压缩比高,能省存储成本。
常见的时序数据库
| 数据库 | 特点 | 适用场景 |
|---|---|---|
| InfluxDB | 开源、SQL-like查询、自带可视化 | 中小团队、快速原型 |
| TimescaleDB | 基于PostgreSQL、支持SQL | 需要复杂查询、与现有系统集成 |
| ClickHouse | 列式存储、极速查询 | 大规模数据分析、实时风控 |
| QuestDB | 高性能、低延迟、Java实现 | 高频交易、低延迟场景 |
我个人习惯用ClickHouse。原因很简单:查询快。做风控的时候,经常需要回测历史数据,ClickHouse的列式存储能秒级返回结果。我在项目中用ClickHouse存了半年的行情数据,大概20TB,查询延迟基本在100ms以内。
数据存储架构
下面这张图是我常用的数据存储架构。它把数据分为热数据、温数据和冷数据三层。
分层的好处很明显:热数据放内存,保证实时风控的响应速度;温数据放ClickHouse,用于回测和监控;冷数据归档到对象存储,节省成本。我曾经见过一个团队,把所有数据都塞进内存,结果服务器内存爆了,风控系统直接挂掉。嗯,分层存储能避免这种悲剧。
数据存储的代码示例
下面是一个用Python写入ClickHouse的示例。我习惯用clickhouse-driver这个库,性能不错。
from clickhouse_driver import Client
# 连接ClickHouse
client = Client(host='localhost', port=9000, database='market_data')
# 创建表(如果不存在)
client.execute('''
CREATE TABLE IF NOT EXISTS orderbook_snapshots (
exchange String,
symbol String,
timestamp UInt64,
bid_price Float64,
ask_price Float64,
bid_size Float64,
ask_size Float64
) ENGINE = MergeTree()
ORDER BY (exchange, symbol, timestamp)
''')
# 批量插入数据
data = [
('binance', 'BTCUSDT', 1705300000000, 42000.5, 42001.0, 1.5, 2.0),
('coinbase', 'BTCUSD', 1705300000000, 41999.8, 42000.3, 0.8, 1.2),
]
client.execute(
'INSERT INTO orderbook_snapshots (exchange, symbol, timestamp, bid_price, ask_price, bid_size, ask_size) VALUES',
data
)
注意:写入时序数据库时,一定要做批量写入。逐条写入的性能很差,每秒可能只能写几百条。批量写入能轻松达到每秒几万条。我一般每100ms攒一批数据,然后一次性写入。
避坑指南
最后,分享几个我踩过的坑:
- 时间戳精度:不同交易所的时间戳精度不一样,有的到秒,有的到毫秒,有的到微秒。统一精度,否则对齐会出问题。
- 数据回放:做回测的时候,一定要用历史数据,而不是实时数据。实时数据有延迟,回测结果会失真。
- 数据备份:时序数据库虽然稳定,但也要定期备份。我曾经因为硬盘故障,丢了3天的数据,那3天的回测全得重做。
数据基础设施这块,说白了就是「脏活累活」。但做好了,风控模型就稳了。下一层,我们聊聊特征工程——怎么从这些数据里提取有用的信号。