数据源与API对接:交易所API(REST/WebSocket)、数据供应商(Reuters/Bloomberg)、模拟撮合引擎搭建
做高频交易,第一件事不是写策略,而是搞定数据。我见过太多人一上来就研究什么神经网络、强化学习,结果连行情数据都接不稳,回测时用的还是假数据。说白了,数据源就是你的弹药库,API就是你的枪。枪都端不稳,打什么仗?
交易所API:REST vs WebSocket
交易所API主要分两种:REST和WebSocket。很多人问我哪个好,我的回答是:两个都得会。
REST API——适合做“查”和“下单”
REST是请求-响应模式。你问一句,它答一句。适合做查询账户余额、提交订单、拉历史K线这些操作。但有个致命问题——慢。一次请求怎么也得几十毫秒,高频场景下根本扛不住。
核心要点:REST只适合做非实时操作。千万别用它来刷行情,否则你会被交易所封IP。
我举个例子,用Python调币安的REST接口拿BTC/USDT的最新价:
import requests
import json
url = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
response = requests.get(url)
data = json.loads(response.text)
print(f"当前价格: {data['price']}")
这段代码很简单,但你看,每次调用都要建立HTTP连接、等待响应。在高频场景下,这种延迟是不可接受的。
WebSocket——真正的实时数据通道
WebSocket是长连接模式。你连上去,交易所就不断给你推数据。延迟可以做到毫秒级,甚至微秒级。这才是高频交易该用的东西。
我个人习惯用WebSocket接实时行情,用REST做交易执行。为什么分开?因为WebSocket的链路如果被大单阻塞,你的订单可能发不出去。嗯,这里要注意——交易指令走独立通道更安全。
来看一个WebSocket订阅深度数据的例子:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
# 处理深度数据
bids = data['b'][:5] # 前5档买单
asks = data['a'][:5] # 前5档卖单
print(f"买一: {bids[0][0]} @ {bids[0][1]}")
print(f"卖一: {asks[0][0]} @ {asks[0][1]}")
def on_error(ws, error):
print(f"连接异常: {error}")
def on_close(ws, close_status_code, close_msg):
print("连接关闭,准备重连...")
def on_open(ws):
# 订阅BTC/USDT的深度数据
subscribe_msg = {
"type": "subscribe",
"channel": "depth",
"symbol": "BTCUSDT"
}
ws.send(json.dumps(subscribe_msg))
ws = websocket.WebSocketApp(
"wss://stream.binance.com:9443/ws",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
避坑指南:我曾经在生产环境遇到过WebSocket断连后没重连,导致策略空跑了3个小时。后来我加了个心跳检测和自动重连机制,每5秒检查一次连接状态。记住,网络不可靠,你的代码必须可靠。
数据供应商:Reuters与Bloomberg
做高频交易,光靠交易所的数据不够。你需要历史数据做回测,需要多市场数据做套利,需要新闻数据做事件驱动。这时候就得找数据供应商了。
| 供应商 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Reuters (Refinitiv) | 外汇、商品数据强;API灵活 | 价格贵;学习曲线陡 | 外汇高频、跨市场套利 |
| Bloomberg | 数据全面;终端好用 | 极其昂贵;API限制多 | 宏观对冲、事件驱动 |
| 其他(Polygon、Quandl等) | 便宜;适合起步 | 数据质量参差不齐 | 个人研究、小团队 |
你想想看,Bloomberg一年要几万美金,Reuters也不便宜。我刚开始做量化时,根本用不起这些。后来发现,其实很多交易所自己就提供历史数据下载,虽然格式乱一点,但胜在免费。
注意:不同供应商的数据格式、时间戳精度、价格精度都不一样。做回测前一定要对齐数据。我曾经因为Reuters和交易所的时间戳差了1秒,导致回测结果完全失真。那叫一个惨。
模拟撮合引擎搭建
这是我最想强调的部分。很多人直接用历史数据来回测,觉得没问题。但你想过没有——你的订单真的能成交在那个价格吗?市场深度够吗?滑点多少?
模拟撮合引擎,说白了就是模拟交易所的订单簿,让你在回测时能更真实地模拟成交。我建议每个做高频的人都要自己搭一个。
核心逻辑
一个简单的撮合引擎需要做三件事:
- 维护订单簿——实时更新买盘和卖盘的深度
- 处理订单——限价单、市价单、撤单
- 计算成交——根据订单簿匹配买卖双方
来看一个简化版的实现:
class MatchingEngine:
def __init__(self):
self.bids = [] # 买单队列,价格降序
self.asks = [] # 卖单队列,价格升序
self.trades = [] # 成交记录
def add_order(self, order):
"""添加订单并尝试撮合"""
if order['side'] == 'buy':
self._match_buy(order)
else:
self._match_sell(order)
def _match_buy(self, buy_order):
"""撮合买单"""
while self.asks and buy_order['quantity'] > 0:
best_ask = self.asks[0]
if buy_order['price'] >= best_ask['price']:
# 可以成交
trade_qty = min(buy_order['quantity'], best_ask['quantity'])
trade_price = best_ask['price']
# 记录成交
self.trades.append({
'price': trade_price,
'quantity': trade_qty,
'time': buy_order['time']
})
# 更新订单
buy_order['quantity'] -= trade_qty
best_ask['quantity'] -= trade_qty
if best_ask['quantity'] == 0:
self.asks.pop(0)
else:
break
# 未成交部分加入订单簿
if buy_order['quantity'] > 0:
self.bids.append(buy_order)
self.bids.sort(key=lambda x: -x['price'])
def _match_sell(self, sell_order):
"""撮合卖单,逻辑类似"""
# 实现略...
pass
关键点:这个引擎虽然简单,但已经能模拟出基本的成交逻辑。实际生产中,你还需要考虑手续费、最小交易量、价格精度限制等。我建议先跑通这个基础版本,再逐步加功能。
知识体系总览
下面这张图概括了本章的核心内容。你可以把它当作一个路线图,看看自己现在在哪个位置。
这张图把本章的三个核心模块串起来了。你看,从左到右,从数据获取到数据验证,是一条完整的链路。我个人建议你按这个顺序来学习,不要跳步。
我的经验:刚开始做高频交易时,我花了一个月时间专门研究API对接和撮合引擎。虽然看起来慢,但后来写策略时几乎没出过数据层面的问题。基础打牢了,上层建筑才稳。
好了,这一章的内容就到这里。数据源和API对接是高频交易的基石,别嫌它枯燥。你想想看,如果连数据都接不稳,后面那些花里胡哨的策略还有什么意义?