Mt5pro-bot2/python/main.py

98 lines
4.1 KiB
Python

import time, yaml
from core.connector import MT5
from risk.manager import RiskManager
from filters.filters import Filters
from strategies import REGISTRY
from notifier.telegram import Telegram, BotState
from utils.indicators import atr
from utils.logger import get_logger
log = get_logger()
def main():
cfg = yaml.safe_load(open("config.yaml", encoding="utf-8"))
bot = cfg["bot"]
mt = MT5(cfg["account"], bot["magic"], bot["deviation"])
if not mt.connect():
raise SystemExit("Impossible de se connecter à MT5")
risk, filt, state = RiskManager(cfg["risk"]), Filters(cfg["filters"]), BotState()
def status():
a = mt.account()
pos = mt.positions()
pnl = sum(p.profit for p in pos)
return (f"Solde {a.balance:.2f} | Equity {a.equity:.2f}\n"
f"Positions: {len(pos)} | PnL flottant {pnl:.2f}\n"
f"{'⏸ PAUSE' if state.paused else '▶️ ACTIF'}"
f"{' | 🛑 ' + risk.halted_reason if risk.halted_reason else ''}")
tg = Telegram(cfg["telegram"], state, status)
strategies = []
for s in cfg["strategies"]:
for sym in s["symbols"]:
strategies.append((sym, REGISTRY[s["name"]](s["timeframe"], s["params"])))
last_bar = {}
tg.send("🚀 Bot démarré")
try:
while True:
if not mt.ensure():
time.sleep(10); continue
a = mt.account()
risk.update(a.balance, a.equity)
if state.close_all:
for p in mt.positions(): mt.close_position(p)
state.close_all, state.paused = False, True
tg.send("✅ Positions fermées, bot en pause")
ok, reason = risk.check(a.equity)
if not ok and not risk.halted_reason:
risk.halted_reason = reason
log.error(f"KILL-SWITCH: {reason}")
for p in mt.positions(): mt.close_position(p)
tg.send(f"🛑 KILL-SWITCH: {reason}\nPositions fermées jusqu'à demain.")
# --- gestion des positions ouvertes (break-even / trailing)
for p in mt.positions():
df = mt.rates(p.symbol, "M15", 100)
t = mt.tick(p.symbol)
if df is None or t is None: continue
price = t.bid if p.type == 0 else t.ask
sl = risk.new_sl(p, atr(df.iloc[:-1], 14).iloc[-1], price)
if sl: mt.modify_sl(p, sl)
# --- recherche de signaux
if not (state.paused or risk.halted_reason):
for sym, strat in strategies:
df = mt.rates(sym, strat.timeframe, 500)
if df is None or len(df) < 250: continue
df = df.iloc[:-1] # barres clôturées uniquement
key = (sym, strat.name)
if last_bar.get(key) == df.time.iloc[-1]: continue
last_bar[key] = df.time.iloc[-1]
sig = strat.on_bar(sym, df)
if not sig: continue
info, tick = mt.info(sym), mt.tick(sym)
good, why = filt.all_ok(sym, info, tick, df)
if not good:
log.info(f"Signal {sym} {strat.name} ignoré: {why}"); continue
if not risk.can_open(mt.positions(), mt.positions(sym)):
continue
lots = risk.lot_size(a.equity, sig.sl_distance, info)
if lots <= 0: continue
px = tick.ask if sig.side == "buy" else tick.bid
sl = px - sig.sl_distance if sig.side == "buy" else px + sig.sl_distance
tp = px + sig.tp_distance if sig.side == "buy" else px - sig.tp_distance
if mt.open_trade(sym, sig.side, lots, sl, tp, f"{strat.name}"):
tg.send(f"📈 {sig.side.upper()} {sym} {lots} lots | {strat.name}\nSL {sl:.5f} TP {tp:.5f}")
time.sleep(bot["poll_seconds"])
except KeyboardInterrupt:
log.info("Arrêt demandé")
finally:
mt.shutdown()
if __name__ == "__main__":
main()