119 lines
4 KiB
Python
119 lines
4 KiB
Python
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")
|