Files
mt5_python_ea_suite/data_downloader.py
T
songkunling 2ad610d8fd add ml
2025-08-14 23:17:12 +08:00

120 lines
3.9 KiB
Python

import os
import pandas as pd
import MetaTrader5 as mt5
from datetime import datetime, timedelta
from time import sleep
# --- 配置 ---
# 要获取的交易品种
SYMBOL = "XAUUSD"
# K线周期
TIMEFRAME = mt5.TIMEFRAME_M1
# 需要获取的数据的总起始日期
TOTAL_START_DATE = "2024-01-01"
# 输出文件名
OUTPUT_FILE = "full_historical_data.parquet"
# MT5单次请求的最大K线数 (保守起见,略低于10万)
CHUNK_SIZE = 90000
# --- 主函数 ---
def download_historical_data():
"""连接MT5,分块下载历史数据,并保存到Parquet文件。"""
print("--- 开始历史数据下载任务 ---")
# 连接到MetaTrader 5
if not mt5.initialize():
print("MT5初始化失败, 请检查终端是否开启。")
mt5.shutdown()
return
print(f"已成功连接到MT5: {mt5.terminal_info()}")
# 将字符串日期转换为datetime对象
start_date_limit = datetime.strptime(TOTAL_START_DATE, '%Y-%m-%d')
# 我们从现在开始,往回获取数据
end_date_of_chunk = datetime.now()
all_rates_df = []
total_bars_fetched = 0
while end_date_of_chunk > start_date_limit:
print(f"正在获取 {end_date_of_chunk.strftime('%Y-%m-%d')} 之前的 {CHUNK_SIZE} 条数据...")
# 从指定日期开始向前获取数据
try:
rates = mt5.copy_rates_from(SYMBOL, TIMEFRAME, end_date_of_chunk, CHUNK_SIZE)
except Exception as e:
print(f"从MT5获取数据时发生错误: {e}")
rates = None
if rates is None or len(rates) == 0:
print("没有更多数据返回,或与服务器通信失败。结束下载。")
break
# 将元组列表转换为DataFrame
rates_df = pd.DataFrame(rates)
all_rates_df.append(rates_df)
# 计算下一次请求的结束日期
# 新的结束点是当前获取到的数据块中最早的时间点
earliest_time_in_chunk = rates_df['time'].iloc[0]
end_date_of_chunk = pd.to_datetime(earliest_time_in_chunk, unit='s')
total_bars_fetched += len(rates)
print(f"已获取 {len(rates)} 条数据。最早的数据点: {end_date_of_chunk.strftime('%Y-%m-%d')}, 总计已获取: {total_bars_fetched}")
# 如果获取到的最早日期已经越过了我们的总起始日期,就停止
if end_date_of_chunk <= start_date_limit:
print("已达到设定的总起始日期,下载完成。")
break
# 短暂休眠,避免过于频繁地请求服务器
sleep(0.5)
# --- 数据处理 ---
if not all_rates_df:
print("未能获取到任何数据,程序退出。")
mt5.shutdown()
return
print("\n--- 开始数据合并与清洗 ---")
# 合并所有数据块
full_df = pd.concat(all_rates_df, ignore_index=True)
print(f"合并后总行数: {len(full_df)}")
# 去除重复数据(基于时间戳)
full_df.drop_duplicates(subset='time', inplace=True)
print(f"去除重复后总行数: {len(full_df)}")
# 将时间戳转换为datetime对象,并设置为索引
full_df['time'] = pd.to_datetime(full_df['time'], unit='s')
# 按时间排序
full_df.sort_values('time', inplace=True)
print("数据已按时间排序。")
# 将time列设为索引
full_df.set_index('time', inplace=True)
# --- 保存文件 ---
try:
full_df.to_parquet(OUTPUT_FILE)
print(f"\n--- 任务成功 ---")
print(f"数据已成功保存到: {os.path.abspath(OUTPUT_FILE)}")
print(f"数据范围: 从 {full_df.index[0]}{full_df.index[-1]}")
print(f"总计K线数量: {len(full_df)}")
except Exception as e:
print(f"保存到Parquet文件失败: {e}")
# 关闭与MetaTrader 5的连接
mt5.shutdown()
if __name__ == "__main__":
download_historical_data()