The Backtesting Data Paywall That Doesn't Exist
Most builders assume that multi-year Binance historical data — the kind you need to backtest a strategy or train a model — sits behind an expensive data vendor. It doesn't. Binance publishes its entire futures price and funding history as free, checksummed ZIP files on a public CDN, with no API key and no rate limits.
This is the exact dataset we use to train our HMM market-regime model: USD-M perpetual futures, 1-hour klines plus funding rates, going back to January 2020. Roughly 56,000 hourly candles per long-lived symbol.
This post shows you precisely where that data lives, the URL patterns to fetch it, the symbol list we train on, the five gotchas that will trip you up, and a copy-paste Python downloader. Then: when to use these raw dumps versus our live API.
Where Binance Historical Data Actually Lives
The archive is data.binance.vision — Binance's own public S3 mirror. It serves one ZIP per symbol per month, for spot and futures, across every interval. For regime work you want USD-M futures (futures/um), 1-hour klines, plus the matching funding-rate series.
The two URL patterns you need:
# 1h OHLCV klines (note the /1h/ interval subpath)
https://data.binance.vision/data/futures/um/monthly/klines/{SYMBOL}/1h/{SYMBOL}-1h-{YYYY-MM}.zip
# 8h funding rate (no interval subpath)
https://data.binance.vision/data/futures/um/monthly/fundingRate/{SYMBOL}/{SYMBOL}-fundingRate-{YYYY-MM}.zip
# Append .CHECKSUM to any URL for its SHA256 (optional integrity check)
https://data.binance.vision/data/futures/um/monthly/klines/BTCUSDT/1h/BTCUSDT-1h-2020-01.zip.CHECKSUMA quick smoke test with curl to confirm a file exists before you script the whole loop:
curl -sI "https://data.binance.vision/data/futures/um/monthly/klines/BTCUSDT/1h/BTCUSDT-1h-2020-01.zip" \
| head -n 1
# HTTP/1.1 200 OKEach kline ZIP unzips to a CSV with the familiar columns: open_time, open, high, low, close, volume, close_time, quote_volume, .... Funding ZIPs carry a calc_time (or fundingTime) column and the rate in the last column.
The 30 Symbols We Train On
Our HMM uses two layers. BTCUSDT and ETHUSDT drive the market-regime model (the broad risk backdrop). All 30 symbols feed a pooled per-coin model so it learns altcoin and meme-coin dynamics, not just majors. Each trains from its own listing date onward.
The exact Binance tickers to download:
- Majors & large caps:
BTCUSDT,ETHUSDT,SOLUSDT,BNBUSDT,XRPUSDT,DOGEUSDT,ADAUSDT,AVAXUSDT,LINKUSDT,DOTUSDT,LTCUSDT,ATOMUSDT - Newer L1s/L2s:
NEARUSDT,FILUSDT,UNIUSDT,AAVEUSDT,SUIUSDT,APTUSDT,ARBUSDT,OPUSDT,INJUSDT,SEIUSDT,TIAUSDT,WLDUSDT,ORDIUSDT - Memes (note the 1000-multiplier tickers):
1000PEPEUSDT,WIFUSDT,1000BONKUSDT,1000FLOKIUSDT,1000SHIBUSDT
You don't need our list to use the archive — every Binance perp is in there. But this is the universe behind the regime model, so it's a sensible default if you want parity with our published signals.
Five Gotchas That Will Bite You
The archive is clean, but the edges are sharp. These five cost us time so they don't have to cost you any:
- 404 means pre-listing, not an error. A missing month simply predates that coin's futures listing. BTC/ETH start 2020-01; most memes only appear mid-2023+. Skip 404s and keep iterating — don't abort the loop.
- The
1000…tickers are scaled.1000PEPEUSDTclose is the price of 1,000 PEPE. Harmless for return/regime features (a constant factor cancels), but divide by 1,000 before joining against spot PEPE price. - Some dumps use microsecond timestamps. Newer files occasionally ship
open_timein microseconds. Guard withif t > 1e13: t //= 1000to normalise everything to milliseconds. - Header rows appear in newer dumps. Older CSVs are headerless; newer ones start with
open_time,open,high,…. Skip any row whose first cell isn't numeric. - The current month is missing. The monthly archive only contains complete months. For the live tail, fall back to the REST API:
GET https://fapi.binance.com/fapi/v1/klines?symbol=BTCUSDT&interval=1h&startTime={ms}&limit=1500and the matching/fapi/v1/fundingRate.
One modelling note: funding events are 8-hourly. We forward-fill them to the hourly grid and annualize (rate × periods_per_year) before feeding the model. Match that if you want feature parity with our regime output.
A Copy-Paste Python Downloader
This pulls every complete month of 1h klines for a symbol, handles 404s and microsecond timestamps, and returns (open_time_ms, close) rows. Funding follows the same shape against the fundingRate path.
import io, zipfile, csv
from datetime import datetime, timezone
import httpx
DUMP = "https://data.binance.vision/data/futures/um/monthly"
def months(start="2020-01"):
y, m = map(int, start.split("-"))
now = datetime.now(timezone.utc)
while (y, m) < (now.year, now.month): # stop before the incomplete current month
yield f"{y:04d}-{m:02d}"
y, m = (y + 1, 1) if m == 12 else (y, m + 1)
def klines(client, symbol):
rows = []
for ym in months():
url = f"{DUMP}/klines/{symbol}/1h/{symbol}-1h-{ym}.zip"
r = client.get(url, timeout=120)
if r.status_code == 404: # month predates listing -- normal
continue
r.raise_for_status()
with zipfile.ZipFile(io.BytesIO(r.content)) as zf:
with zf.open(zf.namelist()[0]) as f:
for row in csv.reader(io.TextIOWrapper(f, "utf-8")):
if not row or row[0] == "open_time":
continue # skip header row
t = int(row[0])
if t > 1e13: # microsecond dumps
t //= 1000
rows.append((t, float(row[4]))) # (open_time_ms, close)
return rows
with httpx.Client() as c:
btc = klines(c, "BTCUSDT")
print(len(btc), "hourly bars") # ~56,000 from 2020-01 to nowNo key, no auth, no quota. The whole 30-symbol universe downloads in a few minutes on a home connection.
Binance Dumps vs Our API: Which Should You Use?
The free dumps and cryptodataapi.com solve different problems. Raw Binance data is unbeatable for bulk price history; it has nothing to say about derived signals. Here's the split:
| Need | Binance dumps | CryptoDataAPI |
|---|---|---|
| Multi-year OHLCV (2020+) | Yes — free, 1h/1m | Last ~30 days (1m) live |
| Funding & OI history | Funding yes; OI no | /backtesting/funding |
| Regime labels & HMM probabilities | No — you'd retrain | /api/v1/quant |
| Point-in-time signal snapshots | No | /backtesting/daily-snapshots |
| Health scores, sentiment, macro | No | /api/v1/daily |
Rule of thumb: pull raw price and funding history from the free Binance archive, and use the API for the things you can't reconstruct from candles — our regime labels, health scores, and the point-in-time snapshot archive that captures what every signal read on a given day.
How to Use This for Backtesting
A practical workflow that combines both sources:
- Backbone: download 1h klines + funding from
data.binance.visionfor your universe back to 2020. This is your price and carry series for any backtest. - Regime overlay: rather than retrain an HMM from scratch, pull point-in-time regime reads from
/backtesting/daily-snapshotsso your backtest sees exactly what the live model saw — no look-ahead bias. - Live tail: for the current incomplete month, gap-fill from the Binance REST API, then switch to the same feeds in production so train and live data match.
If you want to reproduce our regime model end-to-end, the acquisition logic above is the whole input pipeline — everything downstream is feature engineering and the HMM fit. If you just want the output, skip the training entirely and read /api/v1/quant and the 14-basket regime framework directly.
Either way, the data that used to feel like the expensive part is free. The value is in what you build on top of it.
Or skip the pipeline: the hosted archive
Everything this guide builds by hand — deduped 1-minute klines for 450+ Binance markets, volume-verified against the exchange and updated daily — is also available as a hosted archive on the backtest data page. One call to /api/v1/backtesting/archives/index lists every dataset, symbol and date range; /api/v1/backtesting/archives/download returns pre-signed Parquet links you can load straight into pandas. It also covers what the public dumps don't: Hyperliquid perps, hourly funding back to May 2023, liquidations and daily full-market snapshots.



