资讯动态

加密货币免费行情数据源选型与避坑指南

发布时间:2026/10/9 8:22:07 来源:尧图企业网站定制
1. 免费行情数据不是没有而是太散先搞清楚要解决什么问题做量化也好做个人监控面板也好第一步永远绕不开数据。我见过不少朋友上来就急着写策略结果被最基础的“加密货币历史数据和实时行情接口怎么免费搞”这个问题卡住。市面上公开接口看起来很多可真要落地的时候会发现每个源都有各种限制有的只给一年历史、有的限制每秒请求次数、有的结构每天变、有的文档写得像天书。这篇文章就是我基于自己搭数据采集系统时踩过的一堆坑按“选型、实时行情、历史K线、数据质量、避坑”这条线完整讲一遍代码部分也是可以直接抄去改的。先说说这个问题到底难在哪。加密货币不像传统股票数据那么规整全球有几百家交易平台每个平台对同一资产品种的价格、深度、成交笔数都不一样所以“行情”本身没有唯一标准。你要的是什么源、什么粒度、什么级别的历史深度直接决定你能用什么免费接口。很多人一上来就想要“全网最强数据源”但真正的问题永远是“我想要解决什么场景”。我习惯把使用场景分成三类。第一类是回测需要某交易对其历史K线足够深、字段完整比如从某币上线第一天到现在的分钟级数据第二类是实时监控或跑自动化交易机器人需要低延迟、自动重连、能持续跑几个星期不崩的消息通道第三类是个人仪表盘或做研究只需要周期性的快照、多币种价格不要求毫秒级。三类场景对应的方案完全不一样如果混为一谈后面一定会绕晕。我自己见过很典型的例子有人为了省事直接用第三方聚合接口给实盘机器人喂实时数据结果行情延迟好几秒遇到波动大的时候滑点直接吃掉利润。也有人为了回测去找付费数据库其实主流币种的分钟级数据在交易所原生接口里都是免费可拿的完全没必要花那笔钱。这篇文章我不打算给你一个唯一答案而是把“从零搭一套免费行情数据源”的正确思路讲清楚包括怎么评估接口、怎么处理历史和实时两类数据、怎么把数据洗成能入库的干净格式。你只需要按照自己的场景对号入座。2. 主流免费数据源实测横评交易所原生和聚合接口各有什么底牌2.1 三类数据源的基本定位免费行情接口大致分成三类。第一类是交易所原生公开接口例如 Binance、OKX、Bybit、Kraken 这类平台对外开放的行情 API。这类接口最大的优势是数据源头就是交易所自身所以延迟最低、字段最完整K线、深度、逐笔成交、24小时统计一般都有。缺点是每家的参数规范不一样没法用一个统一封装直接通吃而且只能拿到该交易所自己的行情。第二类是第三方聚合数据接口例如 CoinGecko、CoinCap、CoinMarketCap、CryptoCompare。它们把多个交易所的价格汇总成统一价格好处是覆盖面广、一个接口就能拿到上千个币种的行情很适合做个人仪表盘、移动端展示这类场景。缺点是历史数据深度往往有限比如免费档只保留最近一年实时性也比交易所原生差不少通常在秒级甚至几十秒级。第三类是所谓“自建爬虫”和辅助数据源比如手动抓取交易平台网页、用区块链浏览器核对链上数据。这类只建议作为补充不建议当主数据源。因为网页结构一变就崩更难处理好限频和反爬维护成本远超收益。2.2 几家常被拿来当主力的免费接口现状我这两年实际测试下来比较适合做主力源的主要还是几家头部交易平台。Binance 的公开 REST 和 WebSocket 接口文档很规范K线接口支持从币种上线到现在的数据历史深度在主流交易所里算第一梯队OKX 的 REST 接口把现货和合约分开历史K线用 /history-candles 可以拉很老的数据但要注意“当前K线”和“历史K线”是两个端点很多人在这里踩坑Bybit 换新版 v5 接口之后限频策略更细了它的统一账户体系决定了把现货、合约、衍生品的 K线接口都放在同一条路径下面整合起来反而简单。第三方聚合里CoinGecko 的免费额度大概在每分钟几十次请求的档位做展示够用做批量回测明显不足。CoinCap 的限频写得很清楚历史接口能把资产价格拉到多年前但字段相对简单。CoinMarketCap 现在免费档必须要申请 API Key历史数据给得非常有限。CryptoCompare 免费档有月度调用额度限制适合中小体量项目。2.3 一张表理清核心差异为了让你一眼看清差异我把自己实际用过的源整理成了一张对比表数据以我最后测试时的状态为准因为接口策略经常变动手前最好查一下最新文档。数据源类型是否需API Key免费额度特点历史K线深度实时性Binance交易所原生不需要公共接口按权重限频批量接口可覆盖数百币种主流币种自上线至今分钟级齐全很高WebSocket毫秒级OKX交易所原生不需要公共接口按端点限频普通行情每分钟几十次K线和历史K线分离深度大很高Bybit交易所原生不需要公共接口按周期限频需注意单位窗口自币种上线起支持范围查询很高Kraken交易所原生部分公共接口不需要限频文档较细整体宽松OHLC 历史覆盖较早高CoinGecko第三方聚合免费档需要Key约每分钟30次以内免费档仅约1年秒级CoinCap第三方聚合不需要200次/分钟左右可查多年资产价格秒级CryptoCompare第三方聚合免费档需要Key月调用量限制免费档历史粒度较粗秒级CoinMarketCap第三方聚合需要Key免费档额度少历史深度弱秒级2.4 到底怎么选我的判断逻辑你先回答自己三个问题第一数据要不要精确到具体某一笔成交还是只要K线级别第二需要多长的历史第三服务稳定性要求多高。如果做回测优先选交易所原生接口尤其是你要回测的标的恰好是某交易所现货那直接拿那家的原生数据最可靠。比如你想模拟 BTC/USDT 在某个具体价位的撮合情况聚合接口算出来的所谓“全球均价”反而不适合。如果只是做行情看板、资产展示CoinGecko、CoinCap 这类聚合源就够用没必要自己维护一堆连接。还有一个容易被忽略的点免费接口并不意味着没有约束。几乎每个源都有限频而且限频规则很复杂不是“每秒多少次”这么简单而是按请求参数消耗“权重”来算。选型时务必去读一下官方限频文档不要在搭建完整个工程之后才发现数据请求被拒。我后面会专门讲限频相关的坑。3. 实时行情落地的两种写法REST 轮询和 WebSocket 订阅3.1 REST 轮询适合监控面板和低频策略REST 轮询是入门最简单的实现方式思路很直接每隔一定时间向行情接口发起一次 HTTP 请求拿到最新 ticker 数据。对于个人监控场景间隔 1 到 5 秒完全够用。代码如下我已经把超时和异常处理加上了。import requests import time from datetime import datetime, timezone session requests.Session() def fetch_ticker(symbol): url https://api.example.com/api/v3/ticker/bookTicker params {symbol: symbol} resp session.get(url, paramsparams, timeout5) resp.raise_for_status() return resp.json() symbols [BTCUSDT, ETHUSDT] while True: for symbol in symbols: try: data fetch_ticker(symbol) now datetime.now(timezone.utc).isoformat() print(now, data.get(symbol), data.get(bidPrice), data.get(askPrice)) except requests.RequestException as exc: # 需要记录日志而不是直接忽略 print(symbol, error, exc) time.sleep(1)注意几个细节。使用requests.Session()可以复用 TCP 连接减少每次握手开销。轮询间隔不要卡在限频极限上建议留出至少 30% 余量。如果同时监控几十个币种优先优先使用支持批量查询的接口例如一次请求能带上所有交易对而不是逐个发请求。有人为了省代码几十个币种循环请求结果直接撞上限频这是新手最容易犯的问题。3.2 WebSocket 订阅真正低延迟的实时通道如果你的机器人需要秒级以内的响应REST 轮询就不太现实了必须上 WebSocket。WebSocket 和 REST 的区别可以理解成“主动打电话问”和“把电话线接通数据自己送上门”。你只需要订阅一次交易所就会持续把行情推给你延迟普遍在毫秒级。不同平台的 WebSocket 订阅格式会有差异但整体结构类似。下面是标准订阅写法以 BTC/USDT 的成交推送为例import json import websocket url wss://stream.example.com:9443/ws/btcusdtaggTrade def on_message(ws, message): data json.loads(message) print(data.get(p), data.get(q), data.get(T)) ws websocket.WebSocketApp(url, on_messageon_message) ws.run_forever()订阅前一定要看官方文档确认消息字段含义。同样是成交推送有的平台字段叫p价格、q数量有的叫price、size还有的会把时间戳单位标成毫秒有的标成微秒一旦理解错后面所有逻辑都会跟着错。3.3 断线重连和心跳保护用 WebSocket 最怕的不是连不上而是连着连着数据断了程序却毫无感知。平台通常会在服务端检测到客户端长时间不活跃时主动断开连接所以不少文档要求客户端定期发心跳 ping。不同平台的策略不一样有的要求 10 秒左右发一次有的说 30 秒还有的直接通过底层 TCP Keepalive 维持。稳妥做法是订阅后自己加一个定时器每 10 到 15 秒发送一次 ping 帧同时记录最近一次收到消息的时间超过约定阈值就主动重建连接。重连最忌讳上来就无限循环断线后立刻重连会形成“重连风暴”既占用本地资源也可能被服务端暂时封禁 IP。标准做法是带指数退避第一次失败等 1 秒第二次等 2 秒第三次等 4 秒最多等 30 秒或 60 秒直到恢复为止。这样的逻辑放在任何实时数据工程里都通用。3.4 轻量数据落地先存原始消息再说实时行情来的非常快尤其是逐笔成交流如果每次都直接处理业务逻辑很容易堆积。我更推荐先设计一个极简存储层把原始消息先落地哪怕是一个文本文件都行。后续需要做统计、回测、异常分析再从这个原始层读取重放。CREATE TABLE ticker_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, event_time INTEGER NOT NULL, price TEXT NOT NULL, quantity TEXT NOT NULL, raw_json TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );用 SQLite 已经能满足个人体量。这里有一个很重要的经验价格、数量字段在 API 返回里通常都是字符串入库就要保持字符串不要提前转成浮点数。尤其对价格极低的币种浮点数转来转去会把精度丢掉到了清洗阶段想还原都难。我见过有人把 0.00001234 这种价格存成 float结果变成 0.000012340000000001后面做价格比较就会出现莫名其妙的差异。4. 历史K线获取完整拆解时间戳、分页、去重与合并4.1 不同来源能拿到多深的历史历史K线是回测的核心素材。头部交易所原生接口的历史深度通常都能满足个人回测需求。比如 BTCUSDT 这种主流交易对日线数据能追溯到 2017 年甚至更早分钟级数据虽然数据量大但接口也支持按时间范围分批拉取。如果你做的是很老的币种或者早期合约数据可能需要到非标准接口里找甚至需要用多个源拼接。第三方聚合源的历史深度就吃亏很多。CoinGecko 免费档我实测下来只能拿到最近一段有限时间的数据想拿三年前的小时线基本不可能。CoinCap 做得稍好一些能按天粒度拉取多年前资产价格但分钟级别有缺失。记住一个原则想做严谨回测任何第三方聚合源都只能作为辅助和交叉校验不能当唯一依据。4.2 分页拉取K线的核心逻辑K线接口一次性最多只能返回一定数量比如一次 1000 根。如果历史跨度大就必须分段拉取。我用 Binance 格式讲一下这套逻辑基本能改到任何平台import requests base https://api.example.com/api/v3/klines symbol BTCUSDT interval 1h # 用毫秒作为游标 start_ms 1500000000000 end_ms 1500500000000 limit 1000 collected [] while start_ms end_ms: params { symbol: symbol, interval: interval, startTime: start_ms, limit: limit, } resp requests.get(base, paramsparams, timeout15) resp.raise_for_status() batch resp.json() if not batch: break collected.extend(batch) # 用最后一条K线的开盘时间 1 作为下次拉取的起点 last_open_time batch[-1][0] start_ms last_open_time 1这段代码里最关键的是“游标推进”逻辑。如果你不把游标往前推下一次请求还是会从同一个位置返回同样的数据形成死循环。更隐蔽的是如果最后一批返回数据量小于limit说明已经到结尾应该主动 break否则游标可能会卡在最后一根K线上反复循环。4.3 时间戳到底代表什么别搞错K线数组里通常有两个时间字段一个是K线开始时间一个是K线结束时间。不同接口的字段位置不一样。有的平台返回的open_time和close_time都在同一行而有的平台只给一个ts文档会注明它是“该K线的开盘时间”还是“该K线的收盘时间”。我平时处理时统一采用open_time作为K线的唯一标识因为它更符合人直觉一根 1 小时K线代表“从某个时间开始到下一个小时开始”这段区间。如果你把收盘时间当成开盘时间整个序列会往未来平移一个周期回测结果看着很准实际是沾了未来数据的便宜这在量化里是致命错误。4.4 合并多段数据并做连续性验证把多段数据拉回来后不要直接拼接先做一遍去重和排序。API 在边界位置偶尔会返回重复K线也可能因为游标计算问题漏掉一根所以我习惯用 pandas 统一处理import pandas as pd columns [ open_time, open, high, low, close, volume, close_time, quote_volume, trades, taker_base, taker_quote, ignore ] df pd.DataFrame(collected, columnscolumns) df[open_time] pd.to_numeric(df[open_time]) df df.drop_duplicates(subsetopen_time) df df.sort_values(open_time).reset_index(dropTrue) # 检查相邻K线时间差 expected_ms 60 * 60 * 1000 # 1小时线 gap df[open_time].diff().iloc[1:] missing_count (gap ! expected_ms).sum() print(不连续位置数量:, missing_count)连续性检查是必须做的一步。很多真实数据会受平台停盘、币种下线、网络异常影响而出现缺口。如果你拿到的数据中间断了一根后续所有基于时间序列的计算都要格外小心要么补齐、要么剔除不能睁一只眼闭一只眼。5. 数据质量检查别把脏数据直接喂给回测和监控5.1 精度问题小数位和浮点误差是隐性炸弹加密货币价格跨度极大从几万美元的 BTC 到小数点后七八位的狗币都有甚至有些新币价格是 0.000000000001 级别。很多编程语言用双精度浮点数表示这类小数会丢精度比较价格时容易出错。我建议所有涉及价格、数量的计算优先使用字符串原样读取在入库时保持字符串或转为高精度 decimal 类型。只有在最后计算信号、生成统计指标时再显式进行数值转换。如果你非要用浮点数至少要统一用Decimal并指定足够大的精度上下文。这一段值得牢记很多精确到小数点后六位的币种就是被 float 悄悄毁掉的。5.2 缺失数据的补全策略向前填充还是剔除K线数据出现缺口时两种常见处理方式分别为向上填充和删除缺口。向上填充指的是用缺失时间之前最近的一根K线填进去但这会人为造成“虚假成交量”删除缺口则会让时间序列时间轴不连续。到底用哪种取决于你的策略逻辑。如果是简单看趋势、画图删除缺口就行。如果是做严格的回测必须明确缺口属于“无交易时段”还是“数据源漏了数据”。加密市场 7x24 小时交易理论上不存在收盘概念真正的无交易时段往往极端罕见。所以大部分缺口就是数据源缺数据最简单稳妥的处理是标注缺口位置并在回测日志里记录下来而不是悄悄补一个假数据。5.3 用多个源交叉验证而不是盲目相信一个源判断数据到底准不准最有效率的方法是交叉验证。我通常的做法是选一个币种取同一天、同一时段的K线从两个独立数据源分别拉出来对比收盘价和时间戳。如果多数点的偏差在正常买卖价差范围内就说明数据源基本可靠如果明明是同一交易对价格却持续偏差超过几个百分比那就要怀疑其中一个源在搞盘、时间戳错位或线路串了。对于第三方聚合源交叉验证尤其重要。聚合源号称“综合多家交易所价格”但每家算法里各交易所权重不同导致同一时刻的价格和某一真实平台的盘口价格可能差一大截。如果不清楚聚合算法遇到极端行情时报价会离实际成交价很远。5.4 硬分叉和同名代币的历史连续性加密市场有个特色问题硬分叉后可能产生同名或近似名称的新币价格历史并非单一线程连续。比如某个币宣布硬分叉新链和旧链同名有些交易所直接改名或同时上线两个交易对。如果你做历史研究必须搞清楚不同时间段的 K线到底属于哪条链不能简单做序列拼接。这种问题没有通用代码解法只能靠人工梳理公告和时间线至少你要知道存在这个风险别一条道走到黑。6. 我踩过的一些接口坑以及一套比较稳的组合方案6.1 限频不像你想的那么傻权重制与 429 处理大多数交易平台的限频都不是简单统计请求次数而是按每个请求的复杂度给一个权重值。比如你拉深度接口权重可能很高拉一个简单 ticker权重就很低。当你在某个时间窗口内累计权重超过阈值服务端会返回你 429 或者类似的限频错误码。我自己的习惯是把所有公共请求封装成同一个方法在方法里统一处理异常、重试、退避。重试不是无脑重试遇到网络错误可以快速重试两次遇到 429 就必须停得更久比如等待 5 到 30 秒。下面是一个精简的处理框架import time def request_with_retry(fn, retries3, backoff1.0): for attempt in range(retries): try: return fn() except Exception as exc: if attempt retries - 1: raise time.sleep(backoff * (2 ** attempt))6.2 WebSocket 重连的循环陷阱run_forever()看起来会自动重连但它默认在连接断开后是直接退出的。如果你在外面套一个while True循环让它重连就要小心“订阅泄漏”。每次重连后之前的订阅关系已经失效你必须在 on_open 回调里重新发送订阅指令不在初始化时只发一次。实际项目里我建议把“连接状态”和“订阅状态”分开管理。连接成功不代表订阅成功订阅成功不代表每一条消息都成功解析。要有一个监控器定期检查最后收到数据的时间一旦超过 N 秒没有消息就触发重连流程。这套机制看起来不起眼却是实时数据系统能稳定跑一个月的关键。6.3 别把免费数据当成生产级高可用方案免费接口背后没有承诺 SLA服务可用性协议它随时可能调整限频策略甚至下线某个端点。我的原则是免费接口适合自用工具、研究回测、中小规模监控不适合用来跑一个收入依赖极高的策略。如果你在跑实盘机器人至少要有两个独立数据源主用 WebSocket备用 REST。主数据源断掉时立刻切换到备用源或者至少能发告警停机而不是在无声无息中接着下错单。6.4 我个人现在最喜欢的组合方式说了这么多分享一套我自己用得比较顺手的组合。历史回测数据我用交易所原生 K线接口做主体如果某个币种早期数据缺失再找第三方聚合的历史价格快照补齐并在代码里给数据打上来源标记实时行情主力用 WebSocket 接收逐笔成交和深度REST 轮询做兜底心跳每 5 秒检查一次发现主连接状态异常就切换监控和展示数据统一先落 SQLite再由一个轻量查询服务负责读数据采集端和展示端完全解耦。这套方案并不复杂但足够应付绝大多数个人项目。最后提醒一句所有接口的免费策略都在变今天能用的端点下周可能就要加 Key 或换版本。建议你在自己工程里加一个“环境检查脚本”定期跑一遍验证所有关键接口是否仍然可用顺便刷新一下限频文档。等这套基础设施稳定了后面你研究策略、做分析才真正不受数据问题牵绊。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑