行情数据源接入:交易所API协议对比与实战

做市商系统里,行情数据源接入是第一道关卡。说白了,你连数据都拿不到,后面策略再牛也是白搭。今天我们就聊聊交易所API协议的选择、数据格式标准化,以及连接池和心跳管理这些实战细节。

一、三大协议:FIX、WebSocket、REST

我个人习惯把交易所API协议分成三类:FIX、WebSocket、REST。它们各有各的脾气,选错了后面会很难受。

协议 延迟 吞吐量 复杂度 典型场景
FIX 极低(微秒级) 高频做市、机构级交易
WebSocket 低(毫秒级) 中高 实时行情推送、零售交易
REST 高(秒级) 历史数据查询、订单管理

1. FIX协议:老牌劲旅

FIX协议在传统金融领域用了二十多年。我在项目中遇到过一家交易所只支持FIX,当时团队里没人熟悉这个协议,折腾了两周才搞定。FIX的优点在于标准化程度高,字段定义清晰,而且支持会话级别的恢复机制。但缺点也很明显——报文是二进制或纯文本格式,解析起来比JSON麻烦得多。

核心要点:FIX的会话层有严格的序列号管理,断线重连后可以自动补发丢失的报文。这一点在行情数据接入中非常关键。

2. WebSocket:现代主流

现在大部分数字货币交易所都支持WebSocket。为什么?因为它天然支持全双工通信,服务端可以主动推送数据。你想想看,做市商需要实时获取买卖盘口变化,用REST轮询的话,延迟高不说,还会浪费带宽。

我记得有一次帮客户优化行情接入,他们原来用REST每100ms轮询一次,结果交易所直接限流了。改成WebSocket后,延迟从200ms降到了5ms,而且带宽占用减少了80%。

避坑指南:WebSocket虽然好,但要注意心跳机制。我曾经遇到过交易所的WebSocket连接在空闲30秒后自动断开,而我们的心跳间隔设成了60秒,导致频繁断线重连。后来统一改成15秒心跳,问题解决。

3. REST:简单但慢

REST适合做两件事:一是查询历史数据,二是下单。实时行情用REST?别闹了。我见过有人用REST做高频行情,结果服务器CPU全耗在HTTP连接上了。

二、行情数据格式标准化

不同交易所返回的行情数据格式五花八门。有的用JSON,有的用Protobuf,有的甚至用自定义二进制格式。如果不做标准化,你的策略代码会变得非常混乱。

我个人习惯的做法是:在数据接入层统一转换成内部标准格式。比如这样:

// 内部标准行情结构
struct MarketData {
    string symbol;      // 统一交易对名称,如"BTC-USDT"
    int64_t timestamp;  // 纳秒级时间戳
    double bid_price;   // 买一价
    double ask_price;   // 卖一价
    double bid_size;    // 买一量
    double ask_size;    // 卖一量
    double last_price;  // 最新成交价
    double volume;      // 24小时成交量
};

为什么要这么做?因为不同交易所对交易对的命名规则不一样。比如币安叫"BTCUSDT",OKX叫"BTC-USDT",火币叫"btcusdt"。如果不统一,你的策略里到处都是if-else判断,维护起来想死的心都有。

注意:时间戳的精度问题。有的交易所给毫秒级,有的给微秒级,有的甚至给秒级。我建议统一转成纳秒级,因为做市商策略对时间精度要求极高,毫秒级的误差都可能导致滑点。

三、连接池与心跳管理

连接池和心跳管理,听起来简单,但做起来坑很多。我见过一个团队,连接池配置不当,导致行情数据延迟从5ms飙升到500ms,最后发现是连接池满了,新请求在排队等待。

1. 连接池设计原则

  • 按交易所隔离:每个交易所独立连接池,避免一个交易所出问题影响其他交易所。
  • 动态扩容:根据行情数据量自动调整连接数。比如开盘时数据量大,可以多开几个连接。
  • 健康检查:定期检查连接是否可用,发现异常立即重建。

我曾经在项目中用过一个简单的连接池实现:

class ConnectionPool {
    int max_connections;
    int min_connections;
    vector<Connection> connections;
    
    void health_check() {
        for (auto& conn : connections) {
            if (!conn.is_alive()) {
                conn.reconnect();
            }
        }
    }
    
    Connection* get_connection() {
        // 轮询或最小连接数策略
        return &connections[rand() % connections.size()];
    }
};

2. 心跳机制实战

心跳是维持长连接的关键。不同交易所的心跳要求不一样:

交易所 心跳间隔 超时时间 备注
币安 3分钟 10分钟 支持Ping/Pong
OKX 15秒 30秒 需要主动发送心跳
Coinbase 30秒 60秒 自动心跳

经验之谈:心跳间隔不要设得太长。我曾经把心跳设成5分钟,结果交易所那边网络波动了一下,连接断了我们完全不知道,直到策略报错才发现。后来统一改成30秒心跳,配合3次重试,基本没再出过问题。

四、整体架构图

下面这张图展示了行情数据接入的整体流程。从交易所API到内部标准化数据,再到策略引擎,每一步都有讲究。

行情数据接入架构图 交易所A (FIX) 交易所B (WebSocket) 交易所C (REST) 连接池与心跳管理 健康检查 | 自动重连 | 动态扩容 数据格式标准化 统一交易对 | 统一时间戳 | 统一字段名 策略引擎 做市策略 | 风控 | 订单管理 数据源层 接入层 处理层 应用层

嗯,这张图基本把行情数据接入的四个层次说清楚了。从数据源到接入层,再到处理层,最后到应用层,每一层都有明确的职责。

五、实战避坑总结

最后,我把自己踩过的坑总结一下:

  • 协议选择:高频做市用FIX,实时行情用WebSocket,历史数据用REST。别混用。
  • 数据标准化:一定要在接入层做,不要在策略层做。否则策略代码会变得又臭又长。
  • 心跳间隔:建议设为交易所要求的一半。比如交易所要求30秒,你就设15秒,留出容错空间。
  • 连接池大小:不是越大越好。我见过有人开100个连接,结果交易所直接封IP。一般5-10个连接就够了。

一个小技巧:在连接池里加一个监控指标,记录每个连接的延迟和丢包率。一旦发现某个连接异常,自动切换到备用连接。这个功能救过我好几次。

行情数据接入看起来简单,但细节决定成败。希望今天的分享能帮你少走一些弯路。

蓝海数据掘金营,专注资料整理