3. 多币种数据批量下载

任务目标

编写一个Python脚本,从币安公开API批量下载多个加密货币的日线K线数据,统一为DataFrame格式并存储为CSV文件,便于后续量化策略回测使用。

环境依赖

需要 requests、pandas 两个库,安装命令:

pip install requests pandas

核心实现

脚本通过币安REST API的 /api/v3/klines 接口获取K线数据。每次请求最多返回1000根K线,因此采用循环分页拉取全部历史数据。下载完成后统一列名并保存为CSV。


import time
import requests
import pandas as pd
from datetime import datetime, timezone

# 配置区
SYMBOLS = ["BTCUSDT", "ETHUSDT", "BNBUSDT"]  # 要下载的交易对
INTERVAL = "1d"                                # K线周期
START_STR = "2020-01-01"                       # 起始日期
END_STR = "2024-12-31"                         # 结束日期
OUTPUT_DIR = "./history_data"                  # 保存目录

BASE_URL = "https://api.binance.com/api/v3/klines"
COLUMNS = [
    "open_time", "open", "high", "low", "close", "volume",
    "close_time", "quote_volume", "count",
    "taker_buy_volume", "taker_buy_quote_volume", "ignore"
]

def fetch_klines(symbol: str, interval: str, start_ms: int, end_ms: int) -> list:
    """分页拉取全部K线数据"""
    all_rows = []
    current_start = start_ms
    while current_start < end_ms:
        params = {
            "symbol": symbol,
            "interval": interval,
            "startTime": current_start,
            "endTime": end_ms,
            "limit": 1000
        }
        resp = requests.get(BASE_URL, params=params, timeout=10)
        resp.raise_for_status()
        rows = resp.json()
        if not rows:
            break
        all_rows.extend(rows)
        # 下一次请求从最后一根K线的close_time + 1开始
        current_start = rows[-1][6] + 1
        time.sleep(0.2)  # 避免触发频率限制
    return all_rows

def download_symbol(symbol: str) -> pd.DataFrame:
    """下载单个交易对并转为DataFrame"""
    start_dt = datetime.strptime(START_STR, "%Y-%m-%d").replace(tzinfo=timezone.utc)
    end_dt = datetime.strptime(END_STR, "%Y-%m-%d").replace(tzinfo=timezone.utc)
    start_ms = int(start_dt.timestamp() * 1000)
    end_ms = int(end_dt.timestamp() * 1000)

    raw = fetch_klines(symbol, INTERVAL, start_ms, end_ms)
    df = pd.DataFrame(raw, columns=COLUMNS)

    # 统一格式:保留数值列并转换类型
    numeric_cols = ["open", "high", "low", "close", "volume"]
    for col in numeric_cols:
        df[col] = df[col].astype(float)
    df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
    df = df[["open_time", "open", "high", "low", "close", "volume"]]
    return df

def main():
    import os
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    for symbol in SYMBOLS:
        print(f"正在下载 {symbol} ...")
        df = download_symbol(symbol)
        file_path = f"{OUTPUT_DIR}/{symbol}_{INTERVAL}.csv"
        df.to_csv(file_path, index=False)
        print(f"已保存 {len(df)} 条记录 -> {file_path}")

if __name__ == "__main__":
    main()

运行方式

将上述代码保存为 download_history.py,在命令行执行:

python download_history.py

验证方法

运行结束后检查 history_data 目录,应生成3个CSV文件。用以下命令快速验证数据完整性:


import pandas as pd
df = pd.read_csv("history_data/BTCUSDT_1d.csv")
print(df.shape)        # 应约为 (1827, 6),即2020-2024年约5年日线数据
print(df.head(3))      # 查看前3行,open_time应从2020-01-01开始
print(df["close"].isna().sum())  # 应为0,无缺失值

通过条件:每个CSV文件均包含6列(时间、开高低收、成交量),时间范围覆盖设定的起止日期,且无空值。若网络不稳定导致请求失败,脚本会抛出异常,可适当增大 time.sleep 间隔后重试。