数据基础设施:数据源选择、清洗对齐与存储方案

做跨境做市,说白了就是跟数据打交道。你模型再牛,策略再精,数据源要是烂了,一切都是白搭。我见过太多团队,花大把时间调参数,结果问题出在数据上——交易所的行情延迟了、OTC的报价对不齐、新闻舆情根本没解析对。嗯,今天我们就来聊聊数据基础设施怎么搭。

数据源选择:交易所API、OTC报价、新闻舆情

数据源的选择,决定了你风控模型的上限。我个人习惯把数据源分成三类:核心行情、场外报价、另类数据。

交易所API

交易所API是主力。你想想看,做市的核心就是盯盘口、抓成交。我建议至少接入两家以上的交易所,防止单点故障。我在项目中遇到过,某家交易所的WebSocket突然断连,结果我们的报价直接卡了3秒——那3秒的损失,够买好几台服务器了。

常见的交易所API包括:

  • REST API:用于获取历史数据、账户信息、下单。适合低频操作。
  • WebSocket:实时推送行情、成交、订单簿变化。做市必备。
  • FIX协议:机构级交易接口,延迟低,但对接成本高。

这里有个坑:不同交易所的数据格式差异很大。比如Binance的订单簿是bidsasks数组,而Coinbase的则是bidsasks对象。你写解析器的时候,一定要做统一抽象。

核心原则:数据源必须冗余。至少两个独立的数据源,才能交叉验证。

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。

小技巧:我习惯用pandasreindex方法做时间对齐。先创建一个统一的时间索引,然后对每个数据源做asof合并。这样能保证每个时间点都有数据。

数据存储方案:时序数据库

数据清洗完,得存起来。做市数据的特点是:高频、海量、时间敏感。普通的关系型数据库根本扛不住。我建议用时序数据库。

为什么选时序数据库?

你想想看,做市数据每秒可能产生几千条行情记录。如果用MySQL,写入压力大,查询也慢。时序数据库专门为这种场景设计:

  • 高写入吞吐:支持每秒百万级写入。
  • 时间范围查询快:按时间戳索引,查询效率高。
  • 数据压缩:时序数据有规律,压缩比高,能省存储成本。

常见的时序数据库

数据库 特点 适用场景
InfluxDB 开源、SQL-like查询、自带可视化 中小团队、快速原型
TimescaleDB 基于PostgreSQL、支持SQL 需要复杂查询、与现有系统集成
ClickHouse 列式存储、极速查询 大规模数据分析、实时风控
QuestDB 高性能、低延迟、Java实现 高频交易、低延迟场景

我个人习惯用ClickHouse。原因很简单:查询快。做风控的时候,经常需要回测历史数据,ClickHouse的列式存储能秒级返回结果。我在项目中用ClickHouse存了半年的行情数据,大概20TB,查询延迟基本在100ms以内。

数据存储架构

下面这张图是我常用的数据存储架构。它把数据分为热数据、温数据和冷数据三层。

数据存储分层架构 数据源层 交易所API | OTC报价 | 新闻舆情 数据清洗与对齐层 存储层 热数据 Redis / 内存 最近1小时数据 温数据 ClickHouse / InfluxDB 最近30天数据 冷数据 HDFS / S3 历史归档数据

分层的好处很明显:热数据放内存,保证实时风控的响应速度;温数据放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天的回测全得重做。

数据基础设施这块,说白了就是「脏活累活」。但做好了,风控模型就稳了。下一层,我们聊聊特征工程——怎么从这些数据里提取有用的信号。


专注资料整理