""" scripts/download_data.py Usage example: python scripts/download_data.py ^ --symbols EURUSD USDJPY GBPUSD USDCHF AUDUSD NZDUSD ^ --timeframes M15 H1 H4 ^ --start 2016-01-01 ^ --end 2026-01-01 ^ --output data/raw ^ --format parquet """ from __future__ import annotations import argparse import sys from datetime import datetime from pathlib import Path def _find_repo_root(start: Path) -> Path: """Walk up from start until a directory containing `src` is found.""" current = start.resolve() while True: if (current / "src").is_dir(): return current if current.parent == current: break current = current.parent raise RuntimeError("Cannot locate repository root (directory containing 'src').") ROOT = _find_repo_root(Path(__file__).parent) if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from src.data.mt5_fetcher import MT5Config, MT5Fetcher # noqa: E402 DEFAULT_SYMBOLS = ["EURUSD", "USDJPY", "GBPUSD", "USDCHF", "AUDUSD", "NZDUSD"] DEFAULT_TIMEFRAMES = ["M15", "H1", "H4"] def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Download Forex historical data from MT5") parser.add_argument( "--symbols", nargs="+", default=DEFAULT_SYMBOLS, help="List of symbols to download", ) parser.add_argument( "--timeframes", nargs="+", default=DEFAULT_TIMEFRAMES, help="List of timeframes, e.g. M15 H1 H4", ) parser.add_argument( "--start", required=True, help="Start date in YYYY-MM-DD", ) parser.add_argument( "--end", required=True, help="End date in YYYY-MM-DD", ) parser.add_argument( "--output", default="data/raw", help="Output directory", ) parser.add_argument( "--format", choices=["parquet", "csv"], default="parquet", help="File format", ) parser.add_argument( "--terminal-path", default=None, help="Optional MT5 terminal path", ) parser.add_argument( "--login", type=int, default=None, help="Optional MT5 login", ) parser.add_argument( "--password", default=None, help="Optional MT5 password", ) parser.add_argument( "--server", default=None, help="Optional MT5 server name", ) return parser.parse_args() def main() -> None: args = parse_args() start = datetime.fromisoformat(args.start) end = datetime.fromisoformat(args.end) output_dir = Path(args.output) config = MT5Config( terminal_path=args.terminal_path, login=args.login, password=args.password, server=args.server, ) fetcher = MT5Fetcher(config) try: print("[INFO] Initializing MT5...") fetcher.initialize() print("[INFO] MT5 initialized.") for symbol in args.symbols: for timeframe in args.timeframes: print(f"[INFO] Downloading {symbol} {timeframe} ...") df = fetcher.fetch_range( symbol=symbol, timeframe=timeframe, start=start, end=end, ) stats = fetcher.validate_bars(df) save_dir = output_dir / symbol / timeframe saved_path = fetcher.save_dataframe( df=df, output_dir=save_dir, symbol=symbol, timeframe=timeframe, fmt=args.format, ) print( f"[OK] {symbol} {timeframe} | rows={stats['rows']} | " f"start={stats['start']} | end={stats['end']} | " f"dup={stats['duplicate_timestamps']} | " f"nulls={stats['null_count']} | " f"bad_ohlc={stats['bad_ohlc_rows']} | " f"saved={saved_path}" ) print("[DONE] Download completed successfully.") finally: fetcher.shutdown() print("[INFO] MT5 shutdown.") if __name__ == "__main__": main()