一、你真正要接入的是“可信的价格”,不是“能收到的数据”
BTC/ETH 实时行情接入看似简单:拉一个 REST 快照,开一个 WebSocket 订阅,打印一下价格就结束。但只要你准备让它长期运行(比如作为策略信号源、告警触发、或内容站点的数据面板),问题会立刻出现:断线、乱序、重复、偶发异常值、以及“同一时刻不同源给出不同价格”。
所以本文不追求最短 demo,而是给一套生产化的接入思路:先定内部口径,再搭 REST + WS 的双链路,最后用去重/补洞/对账把价格变成可信输入。标题里的 BTC/USD 与 ETH/USD 只是两个样例标的,你把它替换成任意加密交易对也成立。
二、一张图理解系统:REST 做基准,WS 做增量
把接入想成两条流水线:
- REST:用于启动对齐(快照)、断线补洞(补齐窗口)、事后对账(验证 WS 是否漏数据)。
- WebSocket:用于实时增量(持续推送),在低延迟场景下承担“主输出”。
核心原则是:WS 负责快,REST 负责对;两者都必须能落到同一个内部数据模型,否则你永远无法解释“为什么实盘触发了、回放却没有”。
三、内部数据模型(建议你直接照抄这份字段清单)
加密行情最容易踩的坑是“字段看似都有,语义却不一致”。建议从一开始就把下游可见的 tick 写成固定结构:
- symbol:统一成你自己的命名,例如
CRYPTO:BTCUSD/CRYPTO:ETHUSD,不要让交易所/供应商命名泄漏到业务层。 - ts:事件时间(event time),统一 UTC 毫秒。必要时同时保留 recvTs(接收时间)用于测延迟。
- price:你用于策略/看板的价格,建议优先 mid 或 last,但必须写清楚是哪一种。
- bid/ask:如果上游提供,强烈建议保留,用于点差、滑点与异常检测。
- source:固定
"itick",为未来多源扩展预留空间。
你可以很克制:不需要一次把所有字段都收进来,但必须“稳定”。稳定比丰富更重要。
四、用 iTick REST 拉快照(启动对齐 + 对账基准)
启动时的第一个动作不是订阅,而是把状态对齐:你要知道当前价大概在哪、时间戳从哪里开始、以及系统是否处于“新的一天/新的一段交易时段”。
示例(Python,参数与返回字段以 iTick 文档为准):
import requests
BASE = "https://api.itick.org"
TOKEN = "your_api_token"
headers = {"accept": "application/json", "token": TOKEN}
params = {"region": "US", "code": "BTCUSD"}
r = requests.get(f"{BASE}/crypto/tick", params=params, headers=headers, timeout=15)
r.raise_for_status()
data = r.json()
print(data)
工程要点:
- timeout 必须有;重试要指数退避;429/5xx 分开处理。
- 快照只作为“基准”,不要用它驱动高频逻辑,否则你会被限流与延迟卡住。
- 把快照结果记录下来(包括 raw 响应),这对排查数据问题非常关键。
五、WebSocket 订阅(实时增量)与长期运行三件套
实时流能跑不难,能跑一周不掉线才算开始。你需要的不是一段 on_message,而是三件套:
- 心跳:用 ping/pong 或应用层心跳,明确“多久没消息算断线”。
- 自动重连:断线后按退避重连,避免重连风暴;重连成功后要重新订阅。
- 有状态去重:既要去掉重复消息,也要处理乱序消息,至少保证下游看到的是“单调推进”的序列。
订阅示例(伪代码风格):
import json
from websocket import WebSocketApp
WS = "wss://api.itick.org/crypto"
TOKEN = "your_api_token"
last_ts = {"CRYPTO:BTCUSD": 0, "CRYPTO:ETHUSD": 0}
def on_open(ws):
ws.send(json.dumps({"type": "auth", "token": TOKEN}))
ws.send(json.dumps({"type": "subscribe", "symbols": ["CRYPTO:BTCUSD", "CRYPTO:ETHUSD"]}))
def on_message(ws, message):
msg = json.loads(message)
if msg.get("type") != "tick":
return
tick = msg.get("data") or {}
symbol = tick.get("symbol")
ts = int(tick.get("ts", 0))
if symbol and ts and ts > last_ts.get(symbol, 0):
last_ts[symbol] = ts
# 写入:统一模型 -> 入库/缓存 -> 触发规则
app = WebSocketApp(WS, on_open=on_open, on_message=on_message)
app.run_forever(ping_interval=15, ping_timeout=10)
这里的 last_ts 是最小化示意。生产环境建议用“最后确认写入的序号/时间戳 + 可回放的日志”,否则一旦重启你就无法复现系统状态。
六、数据质量:加密行情“偶发异常值”比你想象得常见
加密市场数据质量问题通常不是持续错误,而是偶发尖刺:某个瞬间价格跳到极端值、点差扩大到异常、或某一段时间更新频率突然下降。你不需要上来就做复杂模型,先用三条规则就能过滤掉大部分事故:
- 单点离群:价格相对过去 N 秒/分钟的变化超过阈值(阈值与波动绑定)。
- 频率异常:单位时间内消息量突然下降(可能是连接半死不活)。
- 点差异常:bid/ask 点差超过正常水平时,禁止触发交易类动作,只做展示或降频。
这些规则的目的不是“预测”,而是“保护下游系统不被脏数据击穿”。
七、断线补洞:把“缺口”显式化,别假装不存在
WebSocket 断线是常态。正确做法是:把缺口变成一等公民。
- 断线发生:记录断线时间、最后一个已确认写入的 ts。
- 重连成功:先用 REST 拉取缺口区间(或用历史接口补齐 bar),再恢复 WS 增量。
- 对账:对比补洞结果与 WS 的序列,确认没有“悄悄漏掉”。
你会发现,一旦补洞机制建立,很多诡异问题(回放对不上、策略触发不一致)都会消失。
八、落地场景:行情接入一旦可靠,后面所有文章都能变得更“硬”
当 BTC/ETH 行情链路稳定之后,你可以非常自然地扩展:
- 风控告警:波动突变、点差扩大、价格越界、数据延迟报警。
- 内容看板:在文章页展示关键价格与当日涨跌,做到“观点与数据同屏”。
- 研究回放:把 WS 实时流落库,配合 REST 补洞,形成可复现的数据集。
这也是为什么我建议你把“接入”写成系统,而不是写成一段示例:系统一旦打牢,之后每一篇策略/研究文章都不会再为基础链路重复造轮子。
九、风险提示与边界
本文的目的在于提供研究与工程方法论,不构成任何投资建议。不同资产、不同市场与不同阶段的主导变量会变化,读者应结合自身风险承受能力与合规要求使用数据与结论。
所有 API 接入示例都以 iTick 的接口规范为参考,实际返回字段与参数以官方文档为准。生产环境务必加入超时、重试、监控与告警,并对关键数据做校验与对账。
十、延伸阅读
如果你希望把本文的方法继续扩展到“策略回测→实盘部署”,建议再补齐三块能力:数据存储与回放(保证复现)、参数与版本管理(保证可迭代)、以及风控与告警(保证可长期运行)。当这三块补齐后,你的研究与工程会进入一个稳定的正循环。
对于内容型博客而言,同样建议把“变量、口径、验证、复盘”固化为模板:每周更新一次关键变量,每月回顾一次框架有效性。这样你写的每一篇文章都在为同一套系统积累资产,而不是一次性的观点输出。



