3、数据获取与API使用:主流交易所API介绍(Binance/Coinbase)、获取历史K线数据、数据频率与时间周期选择
做量化交易,数据就是你的弹药库。没有数据,再牛的模型也是纸上谈兵。
我个人习惯把数据获取比作「做饭前的买菜」。菜买不好,厨艺再高也白搭。这一章,我们就来聊聊怎么「买菜」——从主流交易所拿到靠谱的历史K线数据。
3.1 主流交易所API:Binance vs Coinbase
市面上交易所很多,但真正适合做量化研究的,其实就那么几家。我主要用Binance和Coinbase,原因很简单:数据全、文档清晰、社区活跃。
核心观点:选交易所API,看三点——数据质量、请求限制、文档友好度。
3.1.1 Binance API
Binance是目前全球最大的加密货币交易所。它的API设计得很「工程师友好」。我个人觉得,Binance的REST API响应速度很快,而且历史数据覆盖了从2017年至今的所有交易对。
举个例子,获取BTC/USDT的日线数据,只需要一个HTTP请求:
import requests
import pandas as pd
# Binance API端点
url = "https://api.binance.com/api/v3/klines"
# 参数设置
params = {
"symbol": "BTCUSDT",
"interval": "1d", # 日线
"limit": 1000 # 最多1000根K线
}
# 发送请求
response = requests.get(url, params=params)
data = response.json()
# 解析成DataFrame
df = pd.DataFrame(data, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_asset_volume", "number_of_trades",
"taker_buy_base_asset_volume", "taker_buy_quote_asset_volume", "ignore"
])
# 转换时间戳
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
print(df[["open_time", "open", "high", "low", "close", "volume"]].head())
小技巧:Binance的K线数据返回的是Unix毫秒时间戳。记得用pd.to_datetime(..., unit="ms")转换,不然你看到的就是一串数字,毫无意义。
3.1.2 Coinbase API
Coinbase Pro(现在叫Coinbase Advanced Trade)是另一家主流交易所。它的API风格偏RESTful,用起来也很顺手。不过,Coinbase的历史数据只保留最近300根K线,这点和Binance不太一样。
我曾经在项目中同时拉取Binance和Coinbase的数据做对比验证。结果发现,两家交易所的收盘价偶尔会有0.1%左右的偏差。为什么?因为撮合机制和流动性不同。嗯,这里要注意——做回测时,最好只用一家交易所的数据,别混着用。
import requests
# Coinbase API端点
url = "https://api.exchange.coinbase.com/products/BTC-USD/candles"
# 参数:粒度300秒(5分钟)
params = {
"granularity": 300,
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-02T00:00:00Z"
}
response = requests.get(url, params=params)
data = response.json()
# Coinbase返回格式:[time, low, high, open, close, volume]
df = pd.DataFrame(data, columns=["time", "low", "high", "open", "close", "volume"])
df["time"] = pd.to_datetime(df["time"], unit="s")
print(df.head())
避坑指南:我曾经因为Coinbase的API限频没处理好,导致IP被临时封禁。Coinbase对未认证的请求限制是每秒3次,认证后是每秒5次。Binance则是每分钟1200次权重。写代码时一定要加time.sleep()或者用异步请求。
3.2 获取历史K线数据:从原始数据到可用DataFrame
拿到原始数据只是第一步。你想想看,交易所返回的JSON里,时间戳是毫秒数,价格是字符串,成交量是浮点数——这些都需要清洗和转换。
我一般会封装一个通用的数据获取函数,方便后续复用:
def fetch_klines_binance(symbol, interval, start_str, end_str=None):
"""
获取Binance历史K线数据
:param symbol: 交易对,如"BTCUSDT"
:param interval: 时间周期,如"1h", "4h", "1d"
:param start_str: 开始时间,如"2024-01-01"
:param end_str: 结束时间,可选
:return: 清洗后的DataFrame
"""
url = "https://api.binance.com/api/v3/klines"
all_data = []
# 转换为毫秒时间戳
start_ts = int(pd.Timestamp(start_str).timestamp() * 1000)
end_ts = int(pd.Timestamp(end_str).timestamp() * 1000) if end_str else None
while True:
params = {
"symbol": symbol,
"interval": interval,
"startTime": start_ts,
"limit": 1000
}
response = requests.get(url, params=params)
data = response.json()
if not data:
break
all_data.extend(data)
# 更新起始时间戳为最后一条数据的时间
start_ts = data[-1][0] + 1
# 如果指定了结束时间,且已超过,则停止
if end_ts and start_ts > end_ts:
break
# 礼貌性暂停,避免触发限频
time.sleep(0.1)
# 转换为DataFrame
df = pd.DataFrame(all_data, columns=[
"open_time", "open", "high", "low", "close", "volume",
"close_time", "quote_asset_volume", "number_of_trades",
"taker_buy_base_asset_volume", "taker_buy_quote_asset_volume", "ignore"
])
# 数据类型转换
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
df["close"] = df["close"].astype(float)
df["open"] = df["open"].astype(float)
df["high"] = df["high"].astype(float)
df["low"] = df["low"].astype(float)
df["volume"] = df["volume"].astype(float)
# 设置时间索引
df.set_index("open_time", inplace=True)
return df[["open", "high", "low", "close", "volume"]]
个人经验:这个函数我用了两年多,从没出过问题。唯一要注意的是,Binance的startTime参数是包含关系,也就是说,如果你设置startTime=某时间戳,返回的数据会包含这个时间点之后的K线。所以循环里要加1毫秒,避免重复获取同一条数据。
3.3 数据频率与时间周期选择
数据频率的选择,直接决定了你的模型能看到什么「粒度」的市场行为。说白了,就像用显微镜还是用望远镜看市场。
我整理了一张表,方便你快速决策:
| 时间周期 | K线数量/天 | 适用场景 | 我的建议 |
|---|---|---|---|
| 1分钟 (1m) | 1440 | 高频交易、做市策略 | 数据量大,适合短线,但噪声也多 |
| 5分钟 (5m) | 288 | 日内交易、网格策略 | 平衡了细节和噪声,我常用 |
| 1小时 (1h) | 24 | 波段交易、趋势跟踪 | 适合大多数机器学习模型 |
| 4小时 (4h) | 6 | 中长线、趋势判断 | 信号稳定,适合LSTM等序列模型 |
| 1天 (1d) | 1 | 长期投资、基本面分析 | 数据少,适合做宏观判断 |
你可能会问:「那我到底该选哪个周期?」
我的回答是:看你的交易频率和模型复杂度。如果你做日内交易,5分钟或15分钟比较合适。如果你做趋势跟踪,4小时或日线更靠谱。我个人习惯先用1小时数据做特征工程,然后下采样到4小时做模型训练——这样既保留了日内细节,又减少了过拟合风险。
核心原则:数据频率越高,噪声越大,但信号也越丰富。频率越低,信号越平滑,但可能错过关键转折点。没有绝对正确的选择,只有适合你策略的选择。
3.4 本章知识体系:数据获取流程
下面这张图,是我自己画的数据获取流程。你看一遍,基本就明白整个链路了:
这张图的核心逻辑是:先选交易所,再定参数,然后发请求,拿到数据后清洗、转换、存储。如果数据量不够,就循环分页获取。嗯,就是这么简单。
3.5 避坑指南与最佳实践
最后,分享几个我踩过的坑,希望能帮你省点时间:
- 限频问题:我曾经一次性发了2000个请求,结果IP被封了24小时。后来我学乖了,每次请求之间至少间隔100毫秒,并且用
requests.Session()复用连接。 - 数据对齐:不同交易所的K线时间戳可能差几秒。做回测时,一定要统一时间基准。我一般以Binance的时间为准,因为它的数据最全。
- 缺失值处理:有些冷门币种在早期可能没有交易数据。遇到这种情况,我直接填充为前一天的收盘价,或者干脆丢弃。
- 存储格式:我建议用Parquet格式存储历史数据,比CSV快10倍以上,而且压缩率高。如果你用CSV,记得加上
compression='gzip'。
重要提醒:不要在生产环境中直接用API拉取大量历史数据。正确的做法是:先一次性拉取全量数据存到本地,然后每天增量更新。这样既快又稳。
好了,数据获取这部分就聊到这。下一章我们会深入特征工程,看看怎么从这些原始K线数据里挖出有用的信号。记住,数据是基础,但真正让模型赚钱的,是特征。
公众号:蓝海资料掘金营,微信deep3321