import MetaTrader5 as mt5 import logging import json from datetime import datetime from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import smtplib # ================================ # Logging # ================================ logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s" ) log = logging.getLogger() # ================================ # Load Config # ================================ def load_config(filepath="config.json"): log.info(f"Loading configuration file: {filepath}") with open(filepath, "r") as f: config = json.load(f) log.info("Configuration loaded successfully") return config # ================================ # Send Email (Multiple Recipients) # ================================ def send_email(subject, body, smtp_config): recipients = smtp_config["recipient_email"] if isinstance(recipients, str): recipients = [recipients] msg = MIMEMultipart() msg["From"] = smtp_config["sender_email"] msg["To"] = ", ".join(recipients) msg["Subject"] = subject msg.attach(MIMEText(body, "plain")) try: with smtplib.SMTP(smtp_config["smtp_server"], smtp_config["smtp_port"]) as server: server.starttls() server.login(smtp_config["sender_email"], smtp_config["sender_password"]) server.send_message(msg) log.info(f"Email sent to: {', '.join(recipients)}") except Exception as e: log.error(f"Error sending email: {e}") # ================================ # Fetch Yearly Data # ================================ def get_yearly_data(symbol): log.info(f"Fetching yearly data for {symbol}") # Ensure symbol is selected in Market Watch if not mt5.symbol_select(symbol, True): log.warning(f"Could not select symbol {symbol}") return None now = datetime.now() year_start = datetime(now.year, 1, 1) rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_D1, year_start, now) if rates is None or len(rates) == 0: log.warning(f"No historical data for {symbol}") return None # OHLC open_price = rates[0]['open'] high_price = max(r['high'] for r in rates) low_price = min(r['low'] for r in rates) close_price = rates[-1]['close'] range_points = high_price - low_price # Current price tick = mt5.symbol_info_tick(symbol) if tick is None: log.warning(f"No tick data for {symbol}") current_price = None rel_open = rel_high = rel_low = rel_close = None else: # Use last price, fallback to bid if last is zero current_price = tick.last if tick.last != 0 else tick.bid multiplier = 100 if symbol.lower().startswith("xau") else 1 rel_open = (current_price - open_price) * multiplier rel_high = (current_price - high_price) * multiplier rel_low = (current_price - low_price) * multiplier rel_close = (current_price - close_price) * multiplier range_pips = range_points * (100 if symbol.lower().startswith("xau") else 1) log.info(f"{symbol} → Open: {open_price}, High: {high_price}, Low: {low_price}, Close: {close_price}, Current: {current_price}") return { "Open": open_price, "High": high_price, "Low": low_price, "Close": close_price, "RangePoints": range_points, "RangePips": range_pips, "Current": current_price, "RelOpen": rel_open, "RelHigh": rel_high, "RelLow": rel_low, "RelClose": rel_close } # ================================ # Main Function # ================================ def main(): log.info("===== Yearly Data Script Started =====") config = load_config() # Connect to running MT5 (do not reinitialize) if not mt5.initialize(): log.warning(f"MT5 not running or cannot connect: {mt5.last_error()}") else: log.info("Connected to running MT5 terminal") symbols = ["XAUUSD", "USTEC", "US30"] report_lines = ["Yearly Data Report\n"] for sym in symbols: log.info(f"Processing symbol: {sym}") data = get_yearly_data(sym) if data: report_lines.append(f"--- {sym} ---") report_lines.append(f"Yearly Open: {data['Open']}") report_lines.append(f"Yearly High: {data['High']}") report_lines.append(f"Yearly Low: {data['Low']}") report_lines.append(f"Yearly Close: {data['Close']}") report_lines.append(f"Range (Points): {data['RangePoints']}") report_lines.append(f"Range (Pips): {data['RangePips']}") report_lines.append(f"Current Price: {data['Current'] if data['Current'] is not None else 'N/A'}") if data['Current'] is not None: if data['RelOpen'] is not None: report_lines.append(f"Relative to Open: {data['RelOpen']:.2f}") if data['RelHigh'] is not None: report_lines.append(f"Relative to High: {data['RelHigh']:.2f}") if data['RelLow'] is not None: report_lines.append(f"Relative to Low: {data['RelLow']:.2f}") if data['RelClose'] is not None: report_lines.append(f"Relative to Close: {data['RelClose']:.2f}") else: report_lines.append("Current price not available") report_lines.append("\n") else: report_lines.append(f"{sym}: No data available\n") # Send email send_email( subject="Yearly OHLC + Current Position Report", body="\n".join(report_lines), smtp_config=config["email"] ) mt5.shutdown() log.info("===== Yearly Data Script Finished =====") # ================================ # Run Script # ================================ if __name__ == "__main__": main()