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.