mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-07-29 11:47:44 +00:00
120 lines
3.9 KiB
Python
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()
|