第29章 实战案例:从零搭建一个BTC/USDT做市系统
说实话,前面讲了那么多理论,今天终于到了最过瘾的部分——动手搭一套真的做市系统。
我当年第一次搭做市系统的时候,踩了无数坑。代码写好了,跑起来发现订单全被吃掉了,亏了一晚上。嗯,这些经验今天全部分享给你。
29.1 系统整体架构
先看看我们要搭什么。一个完整的BTC/USDT做市系统,核心就这几块:
核心模块清单:
- 行情模块:接收交易所的实时深度和成交数据
- 策略引擎:计算报价、管理订单簿
- 风控模块:检查仓位、资金、价格偏离
- 执行模块:下单、撤单、查单
- 日志模块:记录所有操作,方便复盘
你看,数据从行情模块进来,经过策略引擎计算,风控模块检查没问题,执行模块发到交易所。交易所的成交反馈再回到行情模块,形成闭环。
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