Ssen4life

MQL5 Experts

Specification

//+------------------------------------------------------------------+
//|                                              ScalpingBot_XM.mq5 |
//|              Robot de Scalping — BTC/USD & XAU/USD (Or)         |
//|         Stratégie : EMA Crossover + RSI + MACD + Bollinger      |
//|                      Compatible XM / MetaTrader 5               |
//+------------------------------------------------------------------+
#property copyright   "ScalpingBot XM MT5"
#property version     "2.0"
#property description "Robot scalping multi-indicateurs pour BTC et Or"
#property strict

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>

//--- Objets MT5
CTrade         trade;
CPositionInfo  posInfo;

//=== PARAMÈTRES CONFIGURABLES =========================================

input group "=== GESTION DU RISQUE ==="
input double   InpLotSize        = 0.01;    // Taille du lot (0.01 = micro-lot)
input double   InpTakeProfit     = 30.0;    // Take Profit en pips
input double   InpStopLoss       = 15.0;    // Stop Loss en pips
input double   InpMaxSpread      = 5.0;     // Spread maximum autorisé (pips)
input double   InpRiskPercent    = 1.0;     // % du capital par trade (si AutoLot actif)
input bool     InpAutoLot        = false;   // Calcul automatique du lot selon risque

input group "=== INDICATEURS ==="
input int      InpEMAFast        = 9;       // Période EMA rapide
input int      InpEMASlow        = 21;      // Période EMA lente
input int      InpRSIPeriod      = 14;      // Période RSI
input double   InpRSIBuy        = 35.0;    // RSI seuil achat (survendu)
input double   InpRSISell       = 65.0;    // RSI seuil vente (suracheté)
input int      InpMACDFast       = 12;      // MACD EMA rapide
input int      InpMACDSlow       = 26;      // MACD EMA lente
input int      InpMACDSignal     = 9;       // MACD ligne signal
input int      InpBBPeriod       = 20;      // Bollinger Bands période
input double   InpBBDeviation    = 2.0;     // Bollinger Bands écart-type
input int      InpScoreMin       = 6;       // Score minimum pour ouvrir un trade

input group "=== FILTRE DE SESSION ==="
input bool     InpUseSession     = true;    // Activer filtre horaire
input int      InpSessionStart   = 7;       // Heure début session (GMT)
input int      InpSessionEnd     = 22;      // Heure fin session (GMT)

input group "=== OPTIONS AVANCÉES ==="
input int      InpMagicNumber    = 202400;  // Numéro magique (unique par EA)
input string   InpComment        = "ScalpBot"; // Commentaire des ordres
input bool     InpShowPanel      = true;    // Afficher panneau d'info
input bool     InpAlerts         = true;    // Alertes sur signal fort

//=== VARIABLES GLOBALES ===============================================

// Handles des indicateurs
int handleEMAFast  = INVALID_HANDLE;
int handleEMASlow  = INVALID_HANDLE;
int handleRSI      = INVALID_HANDLE;
int handleMACD     = INVALID_HANDLE;
int handleBB       = INVALID_HANDLE;

// Stats
int    g_totalTrades  = 0;
int    g_wins         = 0;
int    g_losses       = 0;
double g_totalPnL     = 0.0;
double g_maxDrawdown  = 0.0;
double g_peakBalance  = 0.0;

// Timing
datetime g_lastBarTime = 0;
datetime g_startTime   = 0;

// Labels panneau
string PANEL_PREFIX = "SB_";

//+------------------------------------------------------------------+
//| Initialisation                                                    |
//+------------------------------------------------------------------+
int OnInit()
{
   g_startTime    = TimeCurrent();
   g_peakBalance  = AccountInfoDouble(ACCOUNT_BALANCE);

   // Configurer l'objet trade
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetDeviationInPoints(10);
   trade.SetTypeFilling(ORDER_FILLING_IOC);
   trade.LogLevel(LOG_LEVEL_ERRORS);

   // Créer les handles d'indicateurs
   handleEMAFast = iMA(_Symbol, PERIOD_M1, InpEMAFast, 0, MODE_EMA, PRICE_CLOSE);
   handleEMASlow = iMA(_Symbol, PERIOD_M1, InpEMASlow, 0, MODE_EMA, PRICE_CLOSE);
   handleRSI     = iRSI(_Symbol, PERIOD_M1, InpRSIPeriod, PRICE_CLOSE);
   handleMACD    = iMACD(_Symbol, PERIOD_M1, InpMACDFast, InpMACDSlow, InpMACDSignal, PRICE_CLOSE);
   handleBB      = iBands(_Symbol, PERIOD_M1, InpBBPeriod, 0, InpBBDeviation, PRICE_CLOSE);

   if(handleEMAFast == INVALID_HANDLE || handleEMASlow == INVALID_HANDLE ||
      handleRSI == INVALID_HANDLE || handleMACD == INVALID_HANDLE || handleBB == INVALID_HANDLE)
   {
      Alert("ScalpingBot : Erreur création des indicateurs ! Code : ", GetLastError());
      return INIT_FAILED;
   }

   // Vérifier symbole compatible
   string sym = _Symbol;
   if(StringFind(sym,"BTC") < 0 && StringFind(sym,"XAU") < 0 &&
      StringFind(sym,"GOLD") < 0 && StringFind(sym,"BIT") < 0)
   {
      Print("Avertissement : Symbole ", sym, " non optimisé. Le bot fonctionne mais vérifiez vos paramètres.");
   }

   if(InpShowPanel) CreatePanel();

   Print("====================================================");
   Print("  ScalpingBot XM MT5 — Démarré avec succès");
   Print("  Symbole : ", _Symbol, " | Lot : ", InpLotSize);
   Print("  TP : ", InpTakeProfit, " pips | SL : ", InpStopLoss, " pips");
   Print("  Score min : ", InpScoreMin, "/14");
   Print("====================================================");

   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Nettoyage                                                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(handleEMAFast);
   IndicatorRelease(handleEMASlow);
   IndicatorRelease(handleRSI);
   IndicatorRelease(handleMACD);
   IndicatorRelease(handleBB);
   DeletePanel();
   Print("ScalpingBot arrêté. Trades : ", g_totalTrades, 
         " | Wins : ", g_wins, " | P&L : ", DoubleToString(g_totalPnL, 2), " USD");
}

//+------------------------------------------------------------------+
//| Fonction principale — chaque tick                                |
//+------------------------------------------------------------------+
void OnTick()
{
   // Vérifier barres disponibles
   if(Bars(_Symbol, PERIOD_M1) < 60) return;

   // Vérifier spread
   double spreadPips = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) / 10.0;
   if(spreadPips > InpMaxSpread) return;

   // Filtre de session (heure GMT du serveur)
   if(InpUseSession)
   {
      MqlDateTime dt;
      TimeToStruct(TimeCurrent(), dt);
      if(dt.hour < InpSessionStart || dt.hour >= InpSessionEnd) return;
   }

   // Exécuter uniquement sur nouvelle bougie M1
   datetime currentBar = iTime(_Symbol, PERIOD_M1, 0);
   if(currentBar == g_lastBarTime) return;
   g_lastBarTime = currentBar;

   // Lire les indicateurs
   double emaFast[3], emaSlow[3], rsiVal[2];
   double macdMain[2], macdSig[2];
   double bbUpper[2], bbLower[2], bbMid[2];

   if(CopyBuffer(handleEMAFast, 0, 0, 3, emaFast)  < 3) return;
   if(CopyBuffer(handleEMASlow, 0, 0, 3, emaSlow)  < 3) return;
   if(CopyBuffer(handleRSI,     0, 0, 2, rsiVal)   < 2) return;
   if(CopyBuffer(handleMACD,    0, 0, 2, macdMain)  < 2) return;
   if(CopyBuffer(handleMACD,    1, 0, 2, macdSig)   < 2) return;
   if(CopyBuffer(handleBB,      1, 0, 2, bbUpper)  < 2) return;
   if(CopyBuffer(handleBB,      2, 0, 2, bbLower)  < 2) return;
   if(CopyBuffer(handleBB,      0, 0, 2, bbMid)    < 2) return;

   // Indexation MT5 : [0] = barre actuelle, [1] = barre précédente
   double emaFastCur  = emaFast[1];
   double emaFastPrev = emaFast[2];
   double emaSlowCur  = emaSlow[1];
   double emaSlowPrev = emaSlow[2];
   double rsi         = rsiVal[1];
   double macdHist    = macdMain[1] - macdSig[1];
   double price       = iClose(_Symbol, PERIOD_M1, 1);

   //=== SYSTÈME DE SCORING (0–14 points max) ==========================

   int buyScore  = 0;
   int sellScore = 0;

   // EMA Crossover (poids : 5 pts)
   bool goldCross  = (emaFastPrev <= emaSlowPrev && emaFastCur > emaSlowCur);
   bool deathCross = (emaFastPrev >= emaSlowPrev && emaFastCur < emaSlowCur);
   if(goldCross)                buyScore  += 4;
   if(deathCross)               sellScore += 4;
   if(emaFastCur > emaSlowCur)  buyScore  += 1;
   else                         sellScore += 1;

   // RSI (poids : 5 pts)
   if(rsi < InpRSIBuy)          buyScore  += 3;
   else if(rsi < 50.0)          buyScore  += 1;
   if(rsi > InpRSISell)         sellScore += 3;
   else if(rsi > 50.0)          sellScore += 1;
   if(rsi > 75.0)               sellScore += 2; // Extrême suracheté
   if(rsi < 25.0)               buyScore  += 2; // Extrême survendu

   // MACD (poids : 3 pts)
   if(macdHist > 0)             buyScore  += 2;
   else                         sellScore += 2;
   if(macdMain[1] > macdSig[1]) buyScore  += 1;
   else                         sellScore += 1;

   // Bollinger Bands (poids : 4 pts)
   if(price < bbLower[1])       buyScore  += 3;
   else if(price < bbMid[1])    buyScore  += 1;
   if(price > bbUpper[1])       sellScore += 3;
   else if(price > bbMid[1])    sellScore += 1;

   //=== SIGNAL FINAL ==================================================

   int signal = 0;
   if(buyScore  >= InpScoreMin && buyScore  > sellScore + 2) signal =  1;
   if(sellScore >= InpScoreMin && sellScore > buyScore  + 2) signal = -1;

   //=== GESTION DES POSITIONS OUVERTES ================================

   UpdateStats();

   int openPositions = CountMyPositions();

   //=== OUVERTURE DE POSITION =========================================

   if(openPositions == 0 && signal != 0)
   {
      double lot    = CalculateLot();
      double pipVal = _Point * 10;
      double tp, sl;

      if(signal == 1) // ACHAT
      {
         double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         tp = ask + InpTakeProfit * pipVal;
         sl = ask - InpStopLoss  * pipVal;

         if(trade.Buy(lot, _Symbol, ask, sl, tp, InpComment + "_BUY"))
         {
            g_totalTrades++;
            if(InpAlerts && (buyScore >= 10))
               Alert("ScalpBot ACHAT fort — Score : ", buyScore, "/14 | RSI : ", DoubleToString(rsi,1));
            Print("ACHAT ouvert | Prix : ", ask, " | TP : ", tp, " | SL : ", sl,
                  " | Score : ", buyScore, "/14");
         }
      }
      else // VENTE
      {
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         tp = bid - InpTakeProfit * pipVal;
         sl = bid + InpStopLoss  * pipVal;

         if(trade.Sell(lot, _Symbol, bid, sl, tp, InpComment + "_SELL"))
         {
            g_totalTrades++;
            if(InpAlerts && (sellScore >= 10))
               Alert("ScalpBot VENTE forte — Score : ", sellScore, "/14 | RSI : ", DoubleToString(rsi,1));
            Print("VENTE ouverte | Prix : ", bid, " | TP : ", tp, " | SL : ", sl,
                  " | Score : ", sellScore, "/14");
         }
      }
   }

   //=== FERMETURE PAR SIGNAL CONTRAIRE ================================

   if(openPositions > 0 && signal != 0)
   {
      CloseOnReverseSignal(signal);
   }

   //=== MISE À JOUR PANNEAU ===========================================

   if(InpShowPanel)
   {
      UpdatePanel(buyScore, sellScore, signal, rsi, macdHist,
                  emaFastCur > emaSlowCur, spreadPips);
   }
}

//+------------------------------------------------------------------+
//| Calculer la taille du lot                                         |
//+------------------------------------------------------------------+
double CalculateLot()
{
   if(!InpAutoLot) return NormalizeLot(InpLotSize);

   double balance    = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmount = balance * InpRiskPercent / 100.0;
   double pipValue   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   if(pipValue <= 0) return NormalizeLot(InpLotSize);

   double lot = riskAmount / (InpStopLoss * pipValue * 10);
   return NormalizeLot(lot);
}

double NormalizeLot(double lot)
{
   double lotMin  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double lotMax  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   lot = MathRound(lot / lotStep) * lotStep;
   return MathMax(lotMin, MathMin(lotMax, lot));
}

//+------------------------------------------------------------------+
//| Compter les positions ouvertes de ce bot                         |
//+------------------------------------------------------------------+
int CountMyPositions()
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(posInfo.SelectByIndex(i))
         if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
            count++;
   }
   return count;
}

//+------------------------------------------------------------------+
//| Fermer positions sur signal contraire                            |
//+------------------------------------------------------------------+
void CloseOnReverseSignal(int newSignal)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(posInfo.SelectByIndex(i))
      {
         if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
         {
            bool shouldClose = false;
            if(newSignal == 1  && posInfo.PositionType() == POSITION_TYPE_SELL) shouldClose = true;
            if(newSignal == -1 && posInfo.PositionType() == POSITION_TYPE_BUY)  shouldClose = true;
            if(shouldClose)
            {
               trade.PositionClose(posInfo.Ticket());
               Print("Position fermée sur signal contraire | Ticket : ", posInfo.Ticket());
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Mettre à jour les statistiques                                   |
//+------------------------------------------------------------------+
void UpdateStats()
{
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   g_totalPnL     = balance - AccountInfoDouble(ACCOUNT_CREDIT) - 10000.0;
   // Drawdown
   if(balance > g_peakBalance) g_peakBalance = balance;
   double dd = (g_peakBalance > 0) ? (g_peakBalance - balance) / g_peakBalance * 100.0 : 0;
   if(dd > g_maxDrawdown) g_maxDrawdown = dd;
}

//+------------------------------------------------------------------+
//| PANNEAU D'AFFICHAGE                                              |
//+------------------------------------------------------------------+
void CreatePanel()
{
   string name;
   int x = 10, y = 30, w = 220, lineH = 18;

   // Fond
   name = PANEL_PREFIX + "BG";
   ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x - 5);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y - 5);
   ObjectSetInteger(0, name, OBJPROP_XSIZE,  w + 10);
   ObjectSetInteger(0, name, OBJPROP_YSIZE,  lineH * 12 + 10);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrMidnightBlue);
   ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrDodgerBlue);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);

   // Titres des lignes
   string labels[] = {"ScalpingBot XM MT5", "Symbole", "Signal", "Score BUY",
                       "Score SELL", "RSI", "MACD Hist", "EMA 9/21",
                       "Spread", "Trades", "P&L Session", "Drawdown Max"};
   for(int i = 0; i < ArraySize(labels); i++)
   {
      name = PANEL_PREFIX + "LBL" + IntegerToString(i);
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y + i * lineH);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clrSilver);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
      ObjectSetString(0,  name, OBJPROP_FONT, "Consolas");
      ObjectSetString(0,  name, OBJPROP_TEXT, labels[i]);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }

   // Valeurs dynamiques
   for(int i = 0; i < ArraySize(labels); i++)
   {
      name = PANEL_PREFIX + "VAL" + IntegerToString(i);
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x + 120);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y + i * lineH);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
      ObjectSetString(0,  name, OBJPROP_FONT, "Consolas");
      ObjectSetString(0,  name, OBJPROP_TEXT, "---");
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }
}

void UpdatePanel(int buyScore, int sellScore, int signal, double rsi,
                 double macdHist, bool emaAbove, double spread)
{
   string sigText  = (signal == 1) ? "ACHAT" : (signal == -1) ? "VENTE" : "NEUTRE";
   color  sigColor = (signal == 1) ? clrLime : (signal == -1) ? clrTomato : clrSilver;
   double balance  = AccountInfoDouble(ACCOUNT_BALANCE);
   double equity   = AccountInfoDouble(ACCOUNT_EQUITY);
   double sessionPnL = equity - balance; // P&L non réalisé

   string vals[] = {
      _Symbol,
      sigText,
      IntegerToString(buyScore)  + "/14",
      IntegerToString(sellScore) + "/14",
      DoubleToString(rsi, 1),
      DoubleToString(macdHist, 5),
      emaAbove ? "Haussier" : "Baissier",
      DoubleToString(spread, 1) + " pips",
      IntegerToString(g_totalTrades),
      DoubleToString(sessionPnL, 2) + " $",
      DoubleToString(g_maxDrawdown, 2) + " %"
   };
   color colors[] = {
      clrWhite, sigColor, clrLime, clrTomato,
      (rsi > 65) ? clrTomato : (rsi < 35) ? clrLime : clrWhite,
      (macdHist > 0) ? clrLime : clrTomato,
      emaAbove ? clrLime : clrTomato,
      (spread > InpMaxSpread) ? clrTomato : clrWhite,
      clrWhite,
      (sessionPnL >= 0) ? clrLime : clrTomato,
      (g_maxDrawdown > 5) ? clrTomato : clrWhite
   };

   for(int i = 0; i < ArraySize(vals); i++)
   {
      string name = PANEL_PREFIX + "VAL" + IntegerToString(i + 1);
      ObjectSetString(0,  name, OBJPROP_TEXT,  vals[i]);
      ObjectSetInteger(0, name, OBJPROP_COLOR, colors[i]);
   }

   ChartRedraw(0);
}

void DeletePanel()
{
   ObjectsDeleteAll(0, PANEL_PREFIX);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Événement trade — pour compter gains/pertes                     |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest     &request,
                        const MqlTradeResult      &result)
{
   if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
   {
      if(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL)
      {
         HistoryDealSelect(trans.deal);
         double profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT);
         if(profit != 0)
         {
            g_totalPnL += profit;
            if(profit > 0) g_wins++;
            else           g_losses++;
            Print("Trade fermé | Profit : ", DoubleToString(profit, 2),
                  " $ | Wins : ", g_wins, " | Losses : ", g_losses,
                  " | Win Rate : ", (g_wins + g_losses > 0 ?
                  DoubleToString((double)g_wins / (g_wins + g_losses) * 100, 1) : "0"), "%");
         }
      }
   }
}
//+------------------------------------------------------------------+

Project information

Budget
30 - 500 USD

Customer

Placed orders1
Arbitrage count0