指定
//+------------------------------------------------------------------+
//| 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"), "%");
}
}
}
}
//+------------------------------------------------------------------+
项目信息
预算
30 - 500 USD
客户
所下订单1
仲裁计数0