132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
import MetaTrader5 as mt5
|
|
import logging
|
|
import time
|
|
from datetime import datetime
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
import smtplib
|
|
import json
|
|
|
|
# ================================
|
|
# Logging setup
|
|
# ================================
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
|
|
log = logging.getLogger()
|
|
|
|
# ================================
|
|
# Load config
|
|
# ================================
|
|
def load_config(filepath="config.json"):
|
|
with open(filepath, "r") as f:
|
|
return json.load(f)
|
|
|
|
# ================================
|
|
# 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 Position Data
|
|
# ================================
|
|
def fetch_open_positions():
|
|
positions = mt5.positions_get()
|
|
if positions is None or len(positions) == 0:
|
|
log.info("No open positions")
|
|
return []
|
|
|
|
data = []
|
|
for pos in positions:
|
|
tick = mt5.symbol_info_tick(pos.symbol)
|
|
if tick is None:
|
|
current_price = None
|
|
else:
|
|
current_price = tick.last
|
|
|
|
distance_sl = (current_price - pos.sl) if pos.sl else None
|
|
distance_tp = (pos.tp - current_price) if pos.tp else None
|
|
profit = pos.profit
|
|
|
|
relative = "N/A"
|
|
if current_price and pos.volume:
|
|
if pos.type == mt5.ORDER_TYPE_BUY:
|
|
relative = "Above Open" if current_price > pos.price_open else ("Below Open" if current_price < pos.price_open else "At Open")
|
|
elif pos.type == mt5.ORDER_TYPE_SELL:
|
|
relative = "Below Open" if current_price < pos.price_open else ("Above Open" if current_price > pos.price_open else "At Open")
|
|
|
|
data.append({
|
|
"ticket": pos.ticket,
|
|
"symbol": pos.symbol,
|
|
"type": "BUY" if pos.type == mt5.ORDER_TYPE_BUY else "SELL",
|
|
"volume": pos.volume,
|
|
"open_price": pos.price_open,
|
|
"current_price": current_price,
|
|
"sl": pos.sl,
|
|
"tp": pos.tp,
|
|
"distance_sl": distance_sl,
|
|
"distance_tp": distance_tp,
|
|
"profit": profit,
|
|
"relative_to_open": relative
|
|
})
|
|
return data
|
|
|
|
# ================================
|
|
# Main monitoring loop
|
|
# ================================
|
|
def main():
|
|
config = load_config()
|
|
smtp_config = config["email"]
|
|
update_interval = config.get("update_interval_sec", 60) # default 60 sec
|
|
|
|
# Connect to running MT5 terminal
|
|
if not mt5.initialize():
|
|
log.error(f"MT5 initialize failed: {mt5.last_error()}")
|
|
return
|
|
log.info("Connected to MT5 terminal")
|
|
|
|
try:
|
|
while True:
|
|
positions_data = fetch_open_positions()
|
|
if positions_data:
|
|
report_lines = [f"Open Positions Report - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"]
|
|
for p in positions_data:
|
|
report_lines.append(f"Ticket: {p['ticket']}, Symbol: {p['symbol']}, Type: {p['type']}, Volume: {p['volume']}")
|
|
report_lines.append(f"Open Price: {p['open_price']}, Current Price: {p['current_price']}")
|
|
report_lines.append(f"SL: {p['sl']}, TP: {p['tp']}")
|
|
report_lines.append(f"Distance to SL: {p['distance_sl']}, Distance to TP: {p['distance_tp']}")
|
|
report_lines.append(f"Profit: {p['profit']}, Relative to Open: {p['relative_to_open']}\n")
|
|
|
|
log.info("\n".join(report_lines))
|
|
send_email(subject="Live Positions Update", body="\n".join(report_lines), smtp_config=smtp_config)
|
|
else:
|
|
log.info("No positions to report")
|
|
|
|
time.sleep(update_interval)
|
|
except KeyboardInterrupt:
|
|
log.info("Monitoring stopped by user")
|
|
finally:
|
|
mt5.shutdown()
|
|
log.info("MT5 connection closed")
|
|
|
|
# ================================
|
|
# Run
|
|
# ================================
|
|
if __name__ == "__main__":
|
|
main()
|