第十八章:多交易所支持

做市系统做到一定规模,你一定会面临一个问题:要不要接入多个交易所?

我个人习惯是,从一开始就留好接口。别等业务来了再改架构,那会儿改起来真要命。我在项目中见过太多团队,先只接一个交易所,后来要加第二个,结果发现代码里到处都是交易所的硬编码逻辑,改得想哭。

统一接口抽象层

说白了,就是给所有交易所戴上一副「通用面具」。不管底层是币安、OKX 还是 Coinbase,上层代码看到的接口都一样。

核心思路:定义一套标准接口,每个交易所实现自己的适配器。

// 统一交易所接口
public interface ExchangeAdapter {
    // 行情接口
    OrderBook getOrderBook(String symbol);
    Ticker getTicker(String symbol);
    
    // 交易接口
    String placeOrder(Order order);
    boolean cancelOrder(String orderId);
    OrderStatus getOrderStatus(String orderId);
    
    // 账户接口
    Balance getBalance(String asset);
    List<Balance> getAllBalances();
}

每个交易所写一个实现类。比如 BinanceAdapterOkxAdapter。这样上层策略代码完全不用改,换交易所就像换插头一样简单。

我的经验:接口设计时,参数尽量用自定义对象,别用原始类型。比如 Order 对象里包含价格、数量、方向、类型等字段。这样后续加字段不影响已有实现。

交易所差异处理

你以为接口统一了就万事大吉?太天真了。交易所之间的差异,藏在细节里。

常见的差异点:

  • 精度不同:有的交易所价格支持8位小数,有的只支持2位。我曾经因为精度问题,挂单一直失败,查了半天才发现是小数点位数不对。
  • 订单类型:有的支持 IOC(立即成交否则取消),有的只支持 GTC(普通限价单)。
  • 费率结构:Maker 和 Taker 费率不同,VIP 等级不同,甚至有的交易所对特定币种有优惠。
  • 限频规则:有的每秒允许100次请求,有的只有10次。不注意这个,API 会被封。
  • 时间戳格式:有的用毫秒,有的用微秒,有的甚至用字符串。

避坑指南:我曾经在对接一个新交易所时,没仔细看文档,直接用 UTC 时间戳。结果对方要求的是本地时间戳,所有订单都报错。后来我养成了一个习惯:每个交易所适配器里,单独写一个时间转换函数。

处理这些差异,我建议用「配置驱动」的方式。把差异点提取成配置文件,而不是硬编码在代码里。

// 交易所配置示例
{
  "exchange": "binance",
  "symbols": {
    "BTCUSDT": {
      "pricePrecision": 2,
      "qtyPrecision": 5,
      "minNotional": 10.0
    }
  },
  "rateLimit": {
    "perSecond": 10,
    "perMinute": 1200
  },
  "fee": {
    "maker": 0.001,
    "taker": 0.001
  }
}

套利机会识别

多交易所最大的价值是什么?套利。

同一时刻,不同交易所的同一币种价格可能不一样。比如币安上 BTC 卖 50000,OKX 上卖 50010。这10块钱的差价就是套利空间。

套利的核心逻辑:

  1. 实时获取所有交易所的行情数据
  2. 计算价差:价差 = 最高买价 - 最低卖价
  3. 判断是否覆盖成本:价差 > 手续费 + 滑点 + 转账成本
  4. 如果有利可图,同时在低价交易所买入,高价交易所卖出
// 套利机会检测
public class ArbitrageDetector {
    public ArbitrageOpportunity findOpportunity(Map<String, OrderBook> orderBooks) {
        // 找出所有交易所的最优买卖价
        ExchangePrice bestBid = findBestBid(orderBooks);
        ExchangePrice bestAsk = findBestAsk(orderBooks);
        
        double spread = bestBid.price - bestAsk.price;
        double cost = calculateCost(bestAsk.exchange, bestBid.exchange);
        
        if (spread > cost) {
            return new ArbitrageOpportunity(
                bestAsk.exchange,  // 买入交易所
                bestBid.exchange,  // 卖出交易所
                spread - cost      // 预期利润
            );
        }
        return null;
    }
}

注意:套利不是看到价差就冲。要考虑滑点、延迟、转账时间。我曾经看到一个价差,等程序执行完,价差已经消失了,反而亏了手续费。

路由逻辑

路由逻辑解决的是「这个订单该发到哪个交易所」的问题。

简单场景:你只做单一交易所的做市,不需要路由。但如果你同时做多个交易所,就需要一个智能路由器。

路由策略:

  • 流动性优先:哪个交易所深度好,就往哪发。适合大额订单。
  • 费率优先:哪个交易所手续费低,就往哪发。适合高频小单。
  • 延迟优先:哪个交易所响应快,就往哪发。适合抢单策略。
  • 智能路由:综合评估流动性、费率、延迟、当前持仓等因素,动态选择最优交易所。
// 路由决策
public class OrderRouter {
    private List<ExchangeAdapter> exchanges;
    private RoutingStrategy strategy;
    
    public ExchangeAdapter route(Order order) {
        // 根据策略选择最优交易所
        return strategy.select(order, exchanges);
    }
}

// 策略接口
public interface RoutingStrategy {
    ExchangeAdapter select(Order order, List<ExchangeAdapter> exchanges);
}

我个人习惯用「加权评分」的方式做路由。给每个交易所打分,分数最高的胜出。

// 加权评分路由
public class WeightedRouting implements RoutingStrategy {
    @Override
    public ExchangeAdapter select(Order order, List<ExchangeAdapter> exchanges) {
        ExchangeAdapter best = null;
        double bestScore = Double.MIN_VALUE;
        
        for (ExchangeAdapter exchange : exchanges) {
            double score = 0;
            score += exchange.getLiquidityScore(order) * 0.4;  // 流动性权重40%
            score += exchange.getFeeScore(order) * 0.3;        // 费率权重30%
            score += exchange.getLatencyScore() * 0.2;         // 延迟权重20%
            score += exchange.getBalanceScore(order) * 0.1;    // 余额权重10%
            
            if (score > bestScore) {
                bestScore = score;
                best = exchange;
            }
        }
        return best;
    }
}

我的建议:路由逻辑一定要可配置。不同策略、不同权重,最好能通过配置文件动态调整。别写死在代码里,否则每次改策略都要重新部署。

整体架构图

下面这张图展示了多交易所支持的整体架构。从底层交易所到上层策略,每一层各司其职。

多交易所做市系统架构图 做市策略层 智能路由层 统一接口抽象层 (ExchangeAdapter) BinanceAdapter OkxAdapter CoinbaseAdapter 币安交易所 OKX交易所 Coinbase交易所 套利机会检测模块

这张图里,从上到下依次是:策略层决定「做什么」,路由层决定「去哪做」,统一接口层屏蔽「怎么做」,适配器层处理「差异在哪」。套利检测模块横跨所有交易所,实时监控价差。

总结一下:多交易所支持不是简单的「再加一个交易所」。它需要一套完整的架构设计:统一接口、差异处理、套利检测、智能路由。每一步都有坑,但踩过去之后,你的系统会变得非常强大。

嗯,这套架构我用了好几年,在多个项目里验证过。虽然一开始搭建时多花了一些时间,但后续加交易所、改策略都非常轻松。值得投入。


无相订单流研究社 微信Lucian808555