第29章 实战案例:从零搭建一个BTC/USDT做市系统

说实话,前面讲了那么多理论,今天终于到了最过瘾的部分——动手搭一套真的做市系统。

我当年第一次搭做市系统的时候,踩了无数坑。代码写好了,跑起来发现订单全被吃掉了,亏了一晚上。嗯,这些经验今天全部分享给你。

29.1 系统整体架构

先看看我们要搭什么。一个完整的BTC/USDT做市系统,核心就这几块:

核心模块清单:

  • 行情模块:接收交易所的实时深度和成交数据
  • 策略引擎:计算报价、管理订单簿
  • 风控模块:检查仓位、资金、价格偏离
  • 执行模块:下单、撤单、查单
  • 日志模块:记录所有操作,方便复盘
BTC/USDT 做市系统架构图 行情模块 深度/成交数据 策略引擎 报价计算/订单管理 风控模块 仓位/资金检查 执行模块 下单/撤单/查单 日志模块 操作记录/复盘 交易所API WebSocket/REST 数据流 反馈流

你看,数据从行情模块进来,经过策略引擎计算,风控模块检查没问题,执行模块发到交易所。交易所的成交反馈再回到行情模块,形成闭环。

29.2 核心代码实现

我直接给你看最关键的几个模块。代码量不大,但每行都是实战经验。

29.2.1 行情模块

class MarketData:
    """行情数据模块"""
    def __init__(self, symbol='BTCUSDT'):
        self.symbol = symbol
        self.bids = []  # 买单深度
        self.asks = []  # 卖单深度
        self.last_price = 0.0
        self.ws = None
        
    def connect(self):
        """连接交易所WebSocket"""
        url = f'wss://stream.binance.com:9443/ws/{self.symbol.lower()}@depth20@100ms'
        self.ws = create_connection(url)
        print(f'[行情] 已连接 {self.symbol} 深度流')
        
    def update(self):
        """更新深度数据"""
        data = json.loads(self.ws.recv())
        self.bids = [[float(b[0]), float(b[1])] for b in data['bids']]
        self.asks = [[float(a[0]), float(a[1])] for a in data['asks']]
        self.last_price = (self.bids[0][0] + self.asks[0][0]) / 2

实战经验:我建议用深度20档而不是全量深度。全量深度数据太大,处理起来延迟高。20档足够做市用了,带宽也省不少。

29.2.2 策略引擎

class MarketMaker:
    """做市策略引擎"""
    def __init__(self, spread=0.001, order_size=0.01):
        self.spread = spread  # 价差比例,0.1%
        self.order_size = order_size  # 每单数量
        self.active_orders = []  # 当前挂单
        
    def calculate_quotes(self, market):
        """计算买卖报价"""
        mid_price = market.last_price
        
        # 买一价 = 中间价 * (1 - 价差/2)
        bid_price = round(mid_price * (1 - self.spread / 2), 2)
        # 卖一价 = 中间价 * (1 + 价差/2)
        ask_price = round(mid_price * (1 + self.spread / 2), 2)
        
        return bid_price, ask_price
        
    def should_cancel(self, order, market):
        """判断是否需要撤单"""
        # 如果订单偏离当前价格超过0.05%,撤单重下
        threshold = 0.0005
        current_mid = market.last_price
        order_mid = (order['bid'] + order['ask']) / 2
        
        return abs(order_mid - current_mid) / current_mid > threshold

注意:价差设置不能太死板。我见过有人把价差设成固定值,结果行情波动大时,一边被吃光,另一边挂得老远。建议根据波动率动态调整价差。

29.2.3 风控模块

class RiskControl:
    """风控模块"""
    def __init__(self, max_position=1.0, max_drawdown=0.02):
        self.max_position = max_position  # 最大持仓,单位BTC
        self.max_drawdown = max_drawdown  # 最大回撤
        self.initial_balance = 10000  # 初始资金
        
    def check_position(self, current_position):
        """检查仓位是否超限"""
        if abs(current_position) > self.max_position:
            print(f'[风控] 仓位超限!当前: {current_position}, 限制: {self.max_position}')
            return False
        return True
        
    def check_price_deviation(self, quote_price, market_price):
        """检查报价是否偏离市场太远"""
        deviation = abs(quote_price - market_price) / market_price
        if deviation > 0.01:  # 偏离超过1%
            print(f'[风控] 报价偏离过大!报价: {quote_price}, 市场: {market_price}')
            return False
        return True

说实话,风控模块是我最看重的。以前有个同事,策略跑得好好的,突然行情闪崩,他的报价没跟上,结果被套了50个BTC。嗯,从那以后我就在风控里加了价格偏离检查。

29.3 常见问题排查

跑起来容易,跑稳了难。我整理了几个最常见的问题:

问题 现象 原因 解决方案
订单被吃光 挂单后很快成交 价差太小,被套利者盯上 加大价差,或增加最小挂单时间
频繁撤单 订单刚挂上就撤 价格波动大,阈值太敏感 放宽撤单阈值,或增加延迟
成交不对 实际成交价和预期不符 网络延迟,价格已变 使用本地时钟同步,减少延迟
资金不足 下单失败 未及时更新余额 每次成交后立即更新余额

排查思路:遇到问题先看日志。我习惯把所有操作都记下来,包括下单时间、价格、数量、成交情况。复盘时一看日志就知道问题出在哪。

29.4 性能调优实战

做市系统对延迟极其敏感。你想想看,别人比你快1毫秒,你的单子就被吃了。这里分享几个调优技巧:

29.4.1 减少不必要的计算

# ❌ 低效写法
def update_quotes(self):
    for i in range(100):
        price = self.calculate_price(i)  # 每次循环都计算
        self.quotes.append(price)

# ✅ 高效写法
def update_quotes(self):
    # 预计算,避免循环内重复计算
    base_price = self.get_base_price()
    for i in range(100):
        price = base_price + i * 0.01
        self.quotes.append(price)

29.4.2 使用连接池

每次请求都新建连接?太慢了。我建议用连接池:

import requests
from requests.adapters import HTTPAdapter

session = requests.Session()
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=20)
session.mount('https://', adapter)

# 后续所有请求都用这个session
response = session.get('https://api.binance.com/api/v3/ticker/price')

29.4.3 异步处理

同步代码在等待网络响应时会阻塞。用异步可以大幅提升吞吐量:

import asyncio
import aiohttp

async def place_order(session, order):
    async with session.post('https://api.binance.com/api/v3/order', json=order) as resp:
        return await resp.json()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [place_order(session, order) for order in orders]
        results = await asyncio.gather(*tasks)

性能调优清单:

  • 使用本地缓存,减少API调用
  • 批量处理订单,减少网络往返
  • 使用更快的序列化方式(如msgpack代替json)
  • 将计算密集任务放到独立线程
  • 监控GC暂停时间,必要时调整GC参数

29.5 完整运行流程

最后,我把所有模块串起来,看看完整的运行流程:

def main():
    # 初始化各模块
    market = MarketData('BTCUSDT')
    strategy = MarketMaker(spread=0.001, order_size=0.01)
    risk = RiskControl(max_position=1.0)
    executor = OrderExecutor(api_key='your_key', secret='your_secret')
    
    # 连接行情
    market.connect()
    
    while True:
        try:
            # 1. 获取最新行情
            market.update()
            
            # 2. 计算报价
            bid, ask = strategy.calculate_quotes(market)
            
            # 3. 风控检查
            if not risk.check_price_deviation(bid, market.last_price):
                continue
            if not risk.check_price_deviation(ask, market.last_price):
                continue
                
            # 4. 检查现有订单是否需要撤单
            for order in strategy.active_orders:
                if strategy.should_cancel(order, market):
                    executor.cancel_order(order['id'])
                    
            # 5. 下新订单
            executor.place_buy_order(bid, strategy.order_size)
            executor.place_sell_order(ask, strategy.order_size)
            
            # 6. 记录日志
            log_trade(market.last_price, bid, ask)
            
            # 7. 等待下一个周期
            time.sleep(0.1)  # 100ms一个周期
            
        except Exception as e:
            print(f'[错误] {e}')
            time.sleep(1)

重要提醒:上线前一定要用模拟盘跑几天。我见过太多人直接上实盘,结果一天亏掉一个月利润。模拟盘能帮你发现90%的问题。

好了,这就是从零搭建BTC/USDT做市系统的完整过程。代码量不大,但每个细节都值得推敲。记住,做市不是印钞机,是精细活。把风控做好,把延迟降下来,你就能在这个市场里活得很滋润。


无相订单流研究社 微信Lucian808555