144 lines
4.7 KiB
Python
144 lines
4.7 KiB
Python
|
import MetaTrader5 as mt5
|
||
|
import pytz
|
||
|
import json
|
||
|
import os
|
||
|
import smtplib
|
||
|
import pandas as pd
|
||
|
from datetime import datetime, timedelta
|
||
|
from email.mime.text import MIMEText
|
||
|
from email.mime.multipart import MIMEMultipart
|
||
|
from email.mime.base import MIMEBase
|
||
|
from email import encoders
|
||
|
import logging
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Logging Setup
|
||
|
# -----------------------------------
|
||
|
logging.basicConfig(
|
||
|
format="%(asctime)s | %(levelname)s | %(message)s",
|
||
|
level=logging.INFO,
|
||
|
datefmt="%Y-%m-%d %H:%M:%S"
|
||
|
)
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Load Configuration
|
||
|
# -----------------------------------
|
||
|
def load_config(filepath="config.json"):
|
||
|
if not os.path.exists(filepath):
|
||
|
raise FileNotFoundError(f"Config file not found: {filepath}")
|
||
|
with open(filepath, "r") as f:
|
||
|
return json.load(f)
|
||
|
|
||
|
config = load_config()
|
||
|
EMAIL_CONFIG = config["email"]
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Initialize MT5
|
||
|
# -----------------------------------
|
||
|
def initialize_mt5():
|
||
|
logging.info("Initializing MT5 terminal...")
|
||
|
if not mt5.initialize(
|
||
|
path=config["mt5Pathway"],
|
||
|
login=int(config["username"]),
|
||
|
password=config["password"],
|
||
|
server=config["server"]
|
||
|
):
|
||
|
error = mt5.last_error()
|
||
|
logging.error(f"MT5 initialize failed → Code {error[0]}: {error[1]}")
|
||
|
raise RuntimeError(f"MT5 initialize failed: {error}")
|
||
|
logging.info("MT5 successfully connected.")
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Fetch Weekly Data
|
||
|
# -----------------------------------
|
||
|
def fetch_weekly_data(symbol, timeframe=mt5.TIMEFRAME_H1):
|
||
|
tz = pytz.timezone("America/New_York")
|
||
|
|
||
|
# Define last week's start and end (NY time)
|
||
|
now = datetime.now(tz)
|
||
|
monday = now - timedelta(days=now.weekday()) # Start of this week
|
||
|
last_monday = monday - timedelta(days=7) # Start of previous week
|
||
|
last_sunday = monday - timedelta(seconds=1) # End of previous week
|
||
|
|
||
|
logging.info(f"Fetching {symbol} data from {last_monday} to {last_sunday}")
|
||
|
|
||
|
# Pull data from MT5
|
||
|
rates = mt5.copy_rates_range(symbol, timeframe, last_monday, last_sunday)
|
||
|
if rates is None or len(rates) == 0:
|
||
|
logging.warning(f"No data found for {symbol}")
|
||
|
return None
|
||
|
|
||
|
# Convert to DataFrame
|
||
|
df = pd.DataFrame(rates)
|
||
|
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||
|
return df
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Export and Email
|
||
|
# -----------------------------------
|
||
|
def export_and_email(data_dict):
|
||
|
# Create email object
|
||
|
msg = MIMEMultipart()
|
||
|
msg['From'] = EMAIL_CONFIG["sender_email"]
|
||
|
msg['To'] = EMAIL_CONFIG["recipient_email"]
|
||
|
msg['Subject'] = "Weekly Historical Market Data"
|
||
|
|
||
|
body_text = "Attached are last week's OHLC data for XAUUSD, USTEC, and US30."
|
||
|
msg.attach(MIMEText(body_text, "plain"))
|
||
|
|
||
|
# Attach CSV files
|
||
|
for symbol, df in data_dict.items():
|
||
|
if df is not None:
|
||
|
filename = f"{symbol}_last_week.csv"
|
||
|
df.to_csv(filename, index=False)
|
||
|
logging.info(f"Exported {symbol} data → {filename}")
|
||
|
|
||
|
with open(filename, "rb") as f:
|
||
|
attachment = MIMEBase('application', 'octet-stream')
|
||
|
attachment.set_payload(f.read())
|
||
|
encoders.encode_base64(attachment)
|
||
|
attachment.add_header('Content-Disposition', f'attachment; filename={filename}')
|
||
|
msg.attach(attachment)
|
||
|
|
||
|
# Send email
|
||
|
try:
|
||
|
with smtplib.SMTP(EMAIL_CONFIG["smtp_server"], EMAIL_CONFIG["smtp_port"]) as server:
|
||
|
server.starttls()
|
||
|
server.login(EMAIL_CONFIG["sender_email"], EMAIL_CONFIG["sender_password"])
|
||
|
server.send_message(msg)
|
||
|
logging.info(f"Email with historical data sent → {EMAIL_CONFIG['recipient_email']}")
|
||
|
except Exception as e:
|
||
|
logging.error(f"Email failed: {e}")
|
||
|
|
||
|
# -----------------------------------
|
||
|
# Main
|
||
|
# -----------------------------------
|
||
|
def main():
|
||
|
logging.info("===== Starting Weekly Data Extraction =====")
|
||
|
try:
|
||
|
initialize_mt5()
|
||
|
|
||
|
# Use USTEC instead of US100
|
||
|
symbols = ["XAUUSD", "USTEC", "US30"]
|
||
|
all_data = {}
|
||
|
|
||
|
for symbol in symbols:
|
||
|
df = fetch_weekly_data(symbol, mt5.TIMEFRAME_H1)
|
||
|
if df is not None:
|
||
|
logging.info(f"{symbol} → {len(df)} rows fetched")
|
||
|
else:
|
||
|
logging.warning(f"No data fetched for {symbol}")
|
||
|
all_data[symbol] = df
|
||
|
|
||
|
export_and_email(all_data)
|
||
|
|
||
|
except Exception as e:
|
||
|
logging.error(f"Fatal error: {e}")
|
||
|
finally:
|
||
|
logging.info("Shutting down MT5...")
|
||
|
mt5.shutdown()
|
||
|
logging.info("MT5 shutdown complete.")
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|