MarketData Hub All markets · Guides

Loading Historical Market Data in Python (pandas, Backtrader & VectorBT)

Python is where most quantitative research happens. This guide shows how to load our CSV/JSON exports into pandas with a clean datetime index, resample timeframes, and plug the data into Backtrader and VectorBT.

Open the data downloader →

Load a CSV into pandas

Download an instrument as CSV (e.g. EUR/USD 1-minute) and read it with a parsed datetime index:

import pandas as pd

df = pd.read_csv(
    "eurusd_m1.csv",
    parse_dates=["timestamp"],
    index_col="timestamp",
)
df = df.sort_index()           # ensure ascending (exports already are)
print(df.head())

Timestamps are UTC, so you can compare and join instruments without timezone headaches.

Resample to another timeframe

Already have 1-minute candles but want hourly? Resample OHLC correctly:

agg = {"open": "first", "high": "max", "low": "min", "close": "last", "volume": "sum"}
hourly = df.resample("1H").agg(agg).dropna()

Downsampling (finer → coarser) is exact; you can't invent finer data from coarser, so download the granularity you need. See tick vs OHLC.

Load JSON instead

df = pd.read_json("eurusd_m1.json")
df["timestamp"] = pd.to_datetime(df["timestamp"])
df = df.set_index("timestamp").sort_index()

Feed Backtrader

import backtrader as bt

data = bt.feeds.PandasData(dataname=df)
cerebro = bt.Cerebro()
cerebro.adddata(data)
# cerebro.addstrategy(MyStrategy); cerebro.run()

Feed VectorBT

import vectorbt as vbt

price = df["close"]
fast = price.rolling(10).mean()
slow = price.rolling(50).mean()
entries = fast > slow
exits = fast < slow
pf = vbt.Portfolio.from_signals(price, entries, exits, freq="1min")
print(pf.total_return())

Mind look-ahead and survivorship bias when you build signals. Ready? Browse all instruments and grab a file.

Frequently asked questions

How do I load the CSV into pandas?
Use pandas.read_csv with parse_dates=["timestamp"] and index_col="timestamp", then sort_index(). Timestamps are UTC. The JSON export loads with pandas.read_json followed by pd.to_datetime on the timestamp column.
How do I convert 1-minute data to hourly or daily?
Resample with pandas: aggregate open="first", high="max", low="min", close="last", volume="sum". You can only downsample to a coarser timeframe — download the finest granularity you actually need.
Does the data work with Backtrader and VectorBT?
Yes. For Backtrader wrap the DataFrame in bt.feeds.PandasData; for VectorBT pass the close series (and signals) into Portfolio.from_signals. Both expect a clean datetime index, which the exports provide.
Are the timestamps timezone-aware?
Timestamps are in UTC. They parse as naive UTC by default; localize with tz_localize("UTC") if your pipeline needs timezone-aware indexes.

More guides