mt5.copy_rates_range() in MetaTrader5 Python: get bars between two dates
Copies every bar of one symbol and timeframe that opened between two dates into a numpy array. Returns None when the bars can't be read.
Signature
mt5.copy_rates_range(symbol, timeframe, date_from, date_to) What it does
copy_rates_range() takes a start and an end date instead of a count. Use it for a backtest window or a daily report.
Parameters
| Parameter | What it is |
|---|---|
symbol | The symbol name. Required. |
timeframe | A timeframe constant such as mt5.TIMEFRAME_M1, mt5.TIMEFRAME_H1 or mt5.TIMEFRAME_D1. |
date_from | The start of the range. Dates can be datetime objects or seconds since 1970. Make datetime objects with tzinfo=timezone.utc so your own machine's time zone does not shift them, and remember the terminal reads them on the broker's clock. |
date_to | The end of the range, in the same form as date_from. |
What it returns
- A numpy structured array, one row per bar, with the fields
time,open,high,low,close,tick_volume,spreadandreal_volume.timeis the bar's open time in seconds. Noneon failure. Checkmt5.last_error().
Example
from datetime import datetime, timezone
import MetaTrader5 as mt5
if not mt5.initialize():
raise SystemExit(mt5.last_error())
start = datetime(2026, 9, 1, tzinfo=timezone.utc)
end = datetime(2026, 9, 30, tzinfo=timezone.utc)
rates = mt5.copy_rates_range("GBPUSD", mt5.TIMEFRAME_D1, start, end)
if rates is None:
raise SystemExit(mt5.last_error())
print(len(rates), "daily bars")
mt5.shutdown() Common mistakes
- Bar and tick times are the broker's server clock written as if it were UTC. On most servers that is two or three hours ahead of true UTC. Add the broker's offset before you compare them with real UTC times.
- A daily bar opens at the broker's midnight, not at UTC midnight. A range drawn in UTC cuts days in the wrong place.
- A wide range on a small timeframe can hit the "Max. bars in chart" cap and come back short.
The same with Tickwire on Linux
The package needs a Windows terminal on the same machine. Tickwire runs the terminal for you and gives you a REST API, so the code below runs on Linux, macOS or a server.
Tickwire has no range form of the candles route. Ask for bars before your end date, then page back with the oldest bar's time until you pass your start date.
Tickwire's candles route returns up to limit bars (1000 at most) that open at or before startTime, oldest first, as JSON. Each bar has a UTC time and a brokerTime, so there is no clock to guess. MT5 accounts have 21 timeframes, from 1m to 1mn; MT4 accounts have 9.
GET /users/current/accounts/{account_id}/historical-market-data/symbols/{symbol}/timeframes/{timeframe}/candlesGET /users/current/accounts/{account_id}/server-time
import os, requests
API = "https://api.tickwire.io"
h = {"auth-token": os.environ["TW_KEY"]} # your Tickwire key
base = f"{API}/users/current/accounts/{os.environ['TW_ACCOUNT']}"
url = f"{base}/historical-market-data/symbols/GBPUSD/timeframes/1d/candles"
bars = requests.get(url, params={"startTime": "2026-09-30T00:00:00Z", "limit": 40},
headers=h).json()
sept = [b for b in bars if b["time"] >= "2026-09-01"]
print(len(sept), "daily bars") Related functions
- mt5.copy_rates_from() in MetaTrader5 Python: get bars back from a date
- mt5.copy_rates_from_pos() in MetaTrader5 Python: get the latest bars by position
- mt5.copy_ticks_range() in MetaTrader5 Python: get ticks between two dates
- mt5.history_deals_get() in MetaTrader5 Python: read past deals and fills
Related error codes
MT5 from Python, on any machine
One REST API for MT4 and MT5, with idempotent orders and an audit log. Read the MT5 Python API page or the quickstart.