Gr8/monthlyweekly.py
2025-09-10 15:02:31 +03:00

231 lines
8.2 KiB
Python

import MetaTrader5 as mt5
import pytz
import logging
import json
import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime, timedelta
# ======================================================
# Logging
# ======================================================
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
handlers=[logging.StreamHandler()]
)
log = logging.getLogger()
# ======================================================
# Load Config
# ======================================================
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)
# ======================================================
# MT5 Initialization
# ======================================================
def initialize_mt5(config):
log.info("Initializing MetaTrader 5...")
if not mt5.initialize(
path=config["mt5Pathway"],
login=int(config["username"]),
password=config["password"],
server=config["server"]
):
raise RuntimeError(f"MT5 initialize failed: {mt5.last_error()}")
log.info("MT5 connected successfully")
# ======================================================
# Symbol Management
# ======================================================
def ensure_symbol_visible(symbol):
info = mt5.symbol_info(symbol)
if info is None:
log.error(f"Symbol not found: {symbol}")
return False
if not info.visible:
if mt5.symbol_select(symbol, True):
log.info(f"Symbol {symbol} made visible")
else:
log.error(f"Failed to make symbol visible: {symbol}")
return False
return True
# ======================================================
# Price Fetching Helpers
# ======================================================
def get_current_price(symbol):
"""Fetch current midpoint price"""
if not ensure_symbol_visible(symbol):
return None
tick = mt5.symbol_info_tick(symbol)
if tick:
bid = getattr(tick, "bid", 0.0) or 0.0
ask = getattr(tick, "ask", 0.0) or 0.0
if bid > 0 and ask > 0:
return (bid + ask) / 2.0
if getattr(tick, "last", 0.0) > 0:
return float(tick.last)
# fallback
utc_now = datetime.utcnow()
ticks = mt5.copy_ticks_from(symbol, utc_now - timedelta(seconds=30), 50, mt5.COPY_TICKS_ALL)
if ticks is not None and len(ticks) > 0:
last_tick = ticks[-1]
if "last" in last_tick.dtype.names and last_tick["last"] > 0:
return float(last_tick["last"])
return None
def get_price_at_time(symbol, target_dt_ny):
"""Get open price of candle at specific NY time"""
if not ensure_symbol_visible(symbol):
return None
utc_from = target_dt_ny.astimezone(pytz.utc)
utc_to = utc_from + timedelta(minutes=1)
rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_M1, utc_from, utc_to)
if rates is None or len(rates) == 0:
return None
return float(rates[0]["open"])
# ======================================================
# Weekly Open Calculation
# ======================================================
def get_weekly_open(symbol):
"""Monday 00:00 NY open price"""
tz = pytz.timezone("America/New_York")
now_ny = datetime.now(tz)
monday = now_ny - timedelta(days=now_ny.weekday()) # Monday of current week
monday_open = tz.localize(datetime.combine(monday.date(), datetime.min.time()))
return get_price_at_time(symbol, monday_open)
# ======================================================
# Previous Month High/Low
# ======================================================
def get_previous_month_high_low(symbol):
"""Fetch previous month's high and low using D1 candles"""
if not ensure_symbol_visible(symbol):
return None, None
tz = pytz.timezone("America/New_York")
now = datetime.now(tz)
first_day_this_month = tz.localize(datetime(now.year, now.month, 1))
last_day_prev_month = first_day_this_month - timedelta(days=1)
first_day_prev_month = tz.localize(datetime(last_day_prev_month.year, last_day_prev_month.month, 1))
rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_D1, first_day_prev_month.astimezone(pytz.utc), first_day_this_month.astimezone(pytz.utc))
if rates is None or len(rates) == 0:
return None, None
highs = [r['high'] for r in rates]
lows = [r['low'] for r in rates]
return max(highs), min(lows)
# ======================================================
# Email Sending
# ======================================================
def send_email(config, subject, body):
smtp_config = config["email"]
msg = MIMEMultipart()
msg['From'] = smtp_config["sender_email"]
msg['To'] = smtp_config["recipient_email"]
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 {smtp_config['recipient_email']}")
except Exception as e:
log.error(f"Email sending failed: {e}")
# ======================================================
# Main
# ======================================================
def main():
log.info("===== Weekly & Monthly Levels Monitor =====")
# Load configuration
config = load_config()
# Initialize MT5
initialize_mt5(config)
# Symbols to monitor
symbols = ["XAUUSD", "USTEC", "US30"]
tz = pytz.timezone("America/New_York")
now_ny = datetime.now(tz)
email_body = [f"Report Generated at NY Time: {now_ny.strftime('%Y-%m-%d %H:%M:%S')}", ""]
for symbol in symbols:
log.info(f"Processing {symbol}...")
# Current price
current_price = get_current_price(symbol)
if current_price is None:
log.error(f"Cannot fetch current price for {symbol}")
continue
# Weekly open
weekly_open = get_weekly_open(symbol)
# Daily open and close
today_open_time = tz.localize(datetime.combine(now_ny.date(), datetime.min.time()))
daily_open = get_price_at_time(symbol, today_open_time)
# Use last completed D1 candle for daily close
daily_rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 1, 2)
daily_close = float(daily_rates[-2]['close']) if daily_rates is not None and len(daily_rates) >= 2 else None
# Previous month high/low
prev_month_high, prev_month_low = get_previous_month_high_low(symbol)
# Build report
report = [
f"Symbol: {symbol}",
f"Current Price: {current_price:.2f}",
f"Weekly Open (Mon 00:00 NY): {weekly_open if weekly_open else 'N/A'}",
f"Daily Open: {daily_open if daily_open else 'N/A'}",
f"Previous Daily Close: {daily_close if daily_close else 'N/A'}",
f"Prev Month High: {prev_month_high if prev_month_high else 'N/A'}",
f"Prev Month Low: {prev_month_low if prev_month_low else 'N/A'}",
]
# Relative calculations
if weekly_open and current_price:
report.append(f"Δ Current vs Weekly Open: {current_price - weekly_open:.2f}")
if daily_open and current_price:
report.append(f"Δ Current vs Daily Open: {current_price - daily_open:.2f}")
if daily_close and current_price:
report.append(f"Δ Current vs Daily Close: {current_price - daily_close:.2f}")
email_body.extend(report)
email_body.append("") # spacer
# Send email
send_email(config, subject="Weekly & Monthly Market Report", body="\n".join(email_body))
# Shutdown MT5
mt5.shutdown()
log.info("===== Script Finished =====")
# ======================================================
# Run
# ======================================================
if __name__ == "__main__":
try:
main()
except Exception as e:
log.error(f"Fatal error: {e}")
mt5.shutdown()