3. 数据获取:使用CCXT库统一获取多个交易所的订单簿与Ticker数据
做跨市场套利,第一步就是搞定数据。
你想想看,如果连价格都拿不准,那还套什么利?我见过不少新手,策略写得挺漂亮,结果数据源一换,全崩了。说白了,数据获取是整个套利系统的地基。
3.1 为什么选CCXT?
CCXT是一个开源的加密货币交易库。它支持上百家交易所,API接口统一。我个人习惯用它,原因很简单:
- 统一接口:不管你是连币安还是OKX,代码写法几乎一样
- 活跃维护:社区更新快,新交易所支持及时
- Python原生:pip install ccxt 就能用,省心
核心优势:你只需要学会一套API,就能操作上百个交易所。这在跨市场套利场景下,简直是神器。
3.2 安装与初始化
先装包,这个不用多说:
pip install ccxt
然后初始化交易所连接。我一般会这样写:
import ccxt
# 初始化交易所
exchange_binance = ccxt.binance({
'apiKey': '你的API_KEY',
'secret': '你的SECRET_KEY',
'enableRateLimit': True, # 自动限速,防止被封
})
exchange_okx = ccxt.okx({
'apiKey': '你的API_KEY',
'secret': '你的SECRET_KEY',
'password': '你的API密码', # OKX需要这个
'enableRateLimit': True,
})
小技巧:enableRateLimit 一定要设为 True。我曾经因为没开这个,被币安封了半小时IP,那叫一个惨。
3.3 获取Ticker数据
Ticker就是当前市场快照。包含最新价、24小时涨跌、成交量等。做套利时,我主要看最新价和买卖盘口。
# 获取BTC/USDT的Ticker
ticker_binance = exchange_binance.fetch_ticker('BTC/USDT')
ticker_okx = exchange_okx.fetch_ticker('BTC/USDT')
print(f"币安 BTC价格: {ticker_binance['last']}")
print(f"OKX BTC价格: {ticker_okx['last']}")
# 计算价差
spread = ticker_binance['last'] - ticker_okx['last']
print(f"价差: {spread}")
嗯,这里要注意:不同交易所的Ticker字段名可能略有差异。但CCXT帮你统一了,都用 last、bid、ask 这些标准字段。
3.4 获取订单簿数据
订单簿才是套利的核心。它告诉你当前市场上所有的买单和卖单。我一般会获取深度为10或20的订单簿,够用就行,太深了反而影响速度。
# 获取订单簿,深度10
orderbook_binance = exchange_binance.fetch_order_book('BTC/USDT', limit=10)
orderbook_okx = exchange_okx.fetch_order_book('BTC/USDT', limit=10)
# 查看卖一价和买一价
print(f"币安 卖一: {orderbook_binance['asks'][0]}")
print(f"币安 买一: {orderbook_binance['bids'][0]}")
print(f"OKX 卖一: {orderbook_okx['asks'][0]}")
print(f"OKX 买一: {orderbook_okx['bids'][0]}")
避坑指南:我曾经在实盘时,只看了Ticker的最新价就开仓,结果订单簿深度不够,一单下去滑了0.3%。记住:套利一定要看订单簿,别只看Ticker。
3.5 统一数据格式
不同交易所返回的数据结构虽然被CCXT统一了,但为了后续策略处理方便,我习惯再封装一层:
def normalize_ticker(ticker, exchange_name):
return {
'exchange': exchange_name,
'symbol': ticker['symbol'],
'last': ticker['last'],
'bid': ticker['bid'],
'ask': ticker['ask'],
'volume': ticker['baseVolume'],
'timestamp': ticker['timestamp'],
}
def normalize_orderbook(orderbook, exchange_name):
return {
'exchange': exchange_name,
'bids': orderbook['bids'][:10], # 取前10档
'asks': orderbook['asks'][:10],
'timestamp': orderbook['timestamp'],
}
这样做的好处是:后面策略模块不用关心数据是从哪个交易所来的,统一处理就行。
3.6 多交易所并发获取
套利讲究时效性。如果串行获取数据,等拿到第二个交易所的价格时,第一个已经变了。所以必须并发。
我一般用Python的 asyncio 配合 aiohttp 来做。但CCXT本身也支持异步:
import asyncio
import ccxt.async_support as ccxt_async
async def fetch_ticker_async(exchange, symbol):
ticker = await exchange.fetch_ticker(symbol)
await exchange.close()
return ticker
async def main():
binance = ccxt_async.binance()
okx = ccxt_async.okx()
results = await asyncio.gather(
fetch_ticker_async(binance, 'BTC/USDT'),
fetch_ticker_async(okx, 'BTC/USDT'),
)
print(f"币安: {results[0]['last']}")
print(f"OKX: {results[1]['last']}")
asyncio.run(main())
为什么用异步? 串行获取两个交易所数据,网络延迟加起来可能200ms。异步并发,基本就是最慢那个交易所的延迟,通常50ms以内。做高频套利,这150ms的差距就是钱。
3.7 数据缓存与更新频率
别每次策略循环都去拉数据。我建议用本地缓存,定时更新:
import time
from functools import lru_cache
class DataCache:
def __init__(self, ttl=1.0): # 缓存1秒
self.cache = {}
self.ttl = ttl
def get(self, key, fetch_func):
now = time.time()
if key in self.cache:
data, timestamp = self.cache[key]
if now - timestamp < self.ttl:
return data
# 缓存过期,重新获取
data = fetch_func()
self.cache[key] = (data, now)
return data
这样做的好处是:策略循环可以跑得很快,数据更新频率可控。我一般设0.5秒到1秒的TTL,具体看策略对时效性的要求。
3.8 核心逻辑流程图
下面这张图,是我做跨市场套利数据获取的标准流程:
3.9 实战中的坑
最后分享几个我踩过的坑:
- 限频问题:每个交易所都有API调用限制。CCXT的enableRateLimit能帮你控制,但不同交易所的限频策略不一样。我建议在代码里再加一层本地限频。
- 网络超时:跨市场套利经常要连海外交易所。网络波动是家常便饭。一定要加超时重试机制。
- 数据延迟:你拿到的订单簿,可能已经滞后了几百毫秒。做高频套利时,要考虑这个延迟对策略的影响。
我的经验:刚开始做套利时,我直接用Ticker数据开仓,结果被订单簿深度坑了好几次。后来改成只看订单簿前5档,配合滑点预估,才稳定下来。
数据获取这块,说白了就是三个字:快、准、稳。快靠异步并发,准靠CCXT统一接口,稳靠缓存和重试机制。把这三点做好,你的套利系统就成功了一半。