import MetaTrader5 as mt5 import logging from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import smtplib import json # ----------------------------- # Logging # ----------------------------- 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 # ----------------------------- 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}") # ----------------------------- # Update any position (buy/sell) # ----------------------------- def update_position(position_ticket, sl_points=15, tp_points=15, smtp_config=None): position = mt5.positions_get(ticket=position_ticket) if not position: log.error(f"Position {position_ticket} not found") return pos = position[0] symbol_info = mt5.symbol_info(pos.symbol) if not symbol_info: log.error(f"Symbol info not found for {pos.symbol}") return tick = mt5.symbol_info_tick(pos.symbol) if tick is None: log.error(f"No tick data for {pos.symbol}") return point = symbol_info.point # Determine prices based on buy/sell if pos.type == mt5.ORDER_TYPE_BUY: sl_price = tick.bid - sl_points * point # SL below current price tp_price = tick.bid + tp_points * point # TP above current price else: # SELL sl_price = tick.ask + sl_points * point # SL above current price tp_price = tick.ask - tp_points * point # TP below current price request = { "action": mt5.TRADE_ACTION_SLTP, "position": position_ticket, "symbol": pos.symbol, "sl": sl_price, "tp": tp_price, "magic": pos.magic, "comment": "Update SL/TP via Python", } result = mt5.order_send(request) if result.retcode != mt5.TRADE_RETCODE_DONE: log.error(f"Failed to update position {position_ticket}: {result.retcode}") if smtp_config: send_email( subject=f"Failed to update position {position_ticket}", body=f"Position update failed with code {result.retcode} for {pos.symbol}", smtp_config=smtp_config ) else: log.info(f"Position {position_ticket} updated successfully: SL={sl_price}, TP={tp_price}") if smtp_config: send_email( subject=f"Position {position_ticket} updated", body=f"Updated position {position_ticket} ({pos.symbol})\nSL: {sl_price}\nTP: {tp_price}", smtp_config=smtp_config ) return result # ----------------------------- # Example usage # ----------------------------- if __name__ == "__main__": config = load_config("config.json") if not mt5.initialize(): log.error(f"MT5 initialize failed: {mt5.last_error()}") quit() log.info("Connected to MT5 terminal") # Replace with actual position tickets position_tickets = [53171689231, 557416536] for ticket in position_tickets: update_position(ticket, sl_points=150, tp_points=2000, smtp_config=config["email"]) mt5.shutdown() log.info("MT5 shutdown complete")