EA_Gold_Rebond/EA_Gold_Rebond.mq5

269 lines
10 KiB
MQL5

//+------------------------------------------------------------------+
//| EA_Gold_Rebond.mq5 |
//| Trading Bot EMA8 / SMA14 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link "https://forge.mql5.io"
#property version "2.00"
#include <Trade\Trade.mqh>
CTrade trade;
//--- PARAMÈTRES D'ENTRÉE (Inputs modifiables)
input group "--- PARAMÈTRES DE RISQUE & CAPITAL ---"
input double InpRiskPercent = 20.0; // Risque par position (% de l'équité)
input double InpStopLossPercent = 0.33; // Stop Loss en %
input double InpTakeProfitPercent = 1.00; // Take Profit en %
input int InpMaxPositions = 2; // Maximum de positions ouvertes (Add-in max = 2)
input ulong InpMagicNumber = 888148; // Identifiant unique de ce robot
input group "--- TRAILING STOP & BREAK-EVEN ---"
input double InpBERatio = 1.2; // Activation du Break-Even (en R:R, ex: 1.2R)
input double InpTrailingStepPct = 0.16; // Pas d'avancement du Trailing Stop (%)
input group "--- INDICATEURS ---"
input int InpLenEMA = 8; // Période EMA
input int InpLenSMA = 14; // Période SMA
//--- VARIABLES GLOBALES
int handleEMA = INVALID_HANDLE;
int handleSMA = INVALID_HANDLE;
datetime lastBarTime = 0;
int barsInTrend = 0;
bool entryExecutedThisBar = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetMarginMode();
// Initialisation des indicateurs
handleEMA = iMA(_Symbol, _Period, InpLenEMA, 0, MODE_EMA, PRICE_CLOSE);
handleSMA = iMA(_Symbol, _Period, InpLenSMA, 0, MODE_SMA, PRICE_CLOSE);
if(handleEMA == INVALID_HANDLE || handleSMA == INVALID_HANDLE)
{
Print("[ERREUR] Impossible d'initialiser les indicateurs.");
return(INIT_FAILED);
}
lastBarTime = 0;
barsInTrend = 0;
entryExecutedThisBar = false;
Print("[SUCCÈS] EA_Gold_Rebond initialisé avec succès sur ", _Symbol);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(handleEMA != INVALID_HANDLE) IndicatorRelease(handleEMA);
if(handleSMA != INVALID_HANDLE) IndicatorRelease(handleSMA);
Print("[INFO] EA arrêté.");
}
//+------------------------------------------------------------------+
//| Expert tick function (Chaque variation du prix) |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. GESTION CONTINUE DES POSITIONS EXISTANTES (BE & Trailing Stop)
ManagePositions();
// 2. MISE À JOUR DE LA TENDANCE SUR CLÔTURE DE BOUGIE
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
entryExecutedThisBar = false; // Réinitialisation du verrou d'entrée pour la nouvelle bougie
UpdateTrendCount();
}
// 3. VÉRIFICATION DES RESTRICTIONS D'OUVERTURE
if(GetOpenPositionsCount() >= InpMaxPositions) return;
if(entryExecutedThisBar) return;
// 4. RÉCUPÉRATION EN TEMPS RÉEL DE L'EMA ET LA SMA SUR LA BOUGIE EN COURS (Index 0)
double emaVal[1], smaVal[1];
if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) <= 0) return;
if(CopyBuffer(handleSMA, 0, 0, 1, smaVal) <= 0) return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 5. CONDITIONS D'ENTRÉE AU TOUCHÉ (Dès la 3e bougie de tendance)
bool buySignal = (barsInTrend >= 3) && (ask <= emaVal[0] || ask <= smaVal[0]);
bool sellSignal = (barsInTrend <= -3) && (bid >= emaVal[0] || bid >= smaVal[0]);
// 6. EXECUTION DES ORDRES
if(buySignal)
{
double sl = NormalizeDouble(ask * (1.0 - (InpStopLossPercent / 100.0)), _Digits);
double tp = NormalizeDouble(ask * (1.0 + (InpTakeProfitPercent / 100.0)), _Digits);
double lot = CalculateLotSize(ask, sl);
if(lot > 0 && trade.Buy(lot, _Symbol, ask, sl, tp, "EA Rebond Buy"))
{
PrintFormat("[BUY] Ordre exécuté ! Lot: %.2f | Prix: %.2f | SL: %.2f | TP: %.2f", lot, ask, sl, tp);
entryExecutedThisBar = true;
}
}
else if(sellSignal)
{
double sl = NormalizeDouble(bid * (1.0 + (InpStopLossPercent / 100.0)), _Digits);
double tp = NormalizeDouble(bid * (1.0 - (InpTakeProfitPercent / 100.0)), _Digits);
double lot = CalculateLotSize(bid, sl);
if(lot > 0 && trade.Sell(lot, _Symbol, bid, sl, tp, "EA Rebond Sell"))
{
PrintFormat("[SELL] Ordre exécuté ! Lot: %.2f | Prix: %.2f | SL: %.2f | TP: %.2f", lot, bid, sl, tp);
entryExecutedThisBar = true;
}
}
}
//+------------------------------------------------------------------+
//| Met à jour le compteur de bougies en tendance (Bougie fermée) |
//+------------------------------------------------------------------+
void UpdateTrendCount()
{
double emaVal[1], smaVal[1];
if(CopyBuffer(handleEMA, 0, 1, 1, emaVal) <= 0) return;
if(CopyBuffer(handleSMA, 0, 1, 1, smaVal) <= 0) return;
double close1 = iClose(_Symbol, _Period, 1);
bool aboveBoth = (close1 > emaVal[0] && close1 > smaVal[0]);
bool belowBoth = (close1 < emaVal[0] && close1 < smaVal[0]);
if(aboveBoth)
{
barsInTrend = (barsInTrend > 0) ? barsInTrend + 1 : 1;
}
else if(belowBoth)
{
barsInTrend = (barsInTrend < 0) ? barsInTrend - 1 : -1;
}
else
{
barsInTrend = 0;
}
}
//+------------------------------------------------------------------+
//| Compte le nombre de positions ouvertes du robot |
//+------------------------------------------------------------------+
int GetOpenPositionsCount()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Gestion du Break-Even à 1,2 R et Trailing Stop tous les 0,16 % |
//+------------------------------------------------------------------+
void ManagePositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) != _Symbol || PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
continue;
long posType = PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double targetBE = openPrice * (InpStopLossPercent / 100.0) * InpBERatio;
double stepDist = openPrice * (InpTrailingStepPct / 100.0);
if(posType == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Break-Even à 1,2 R
if(bid >= (openPrice + targetBE) && currentSL < openPrice)
{
trade.PositionModify(ticket, NormalizeDouble(openPrice, _Digits), currentTP);
PrintFormat("[BE] Position Buy #%d sécurisée au Break-Even", ticket);
}
// Trailing Stop par paliers de 0.16%
else if(currentSL >= openPrice)
{
double candidateSL = NormalizeDouble(bid * (1.0 - (InpStopLossPercent / 100.0)), _Digits);
if(candidateSL >= currentSL + stepDist)
{
trade.PositionModify(ticket, candidateSL, currentTP);
PrintFormat("[TRAILING] SL Buy #%d mis à jour : %.2f", ticket, candidateSL);
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// Break-Even à 1,2 R
if(ask <= (openPrice - targetBE) && (currentSL > openPrice || currentSL == 0))
{
trade.PositionModify(ticket, NormalizeDouble(openPrice, _Digits), currentTP);
PrintFormat("[BE] Position Sell #%d sécurisée au Break-Even", ticket);
}
// Trailing Stop par paliers de 0.16%
else if(currentSL > 0 && currentSL <= openPrice)
{
double candidateSL = NormalizeDouble(ask * (1.0 + (InpStopLossPercent / 100.0)), _Digits);
if(candidateSL <= currentSL - stepDist)
{
trade.PositionModify(ticket, candidateSL, currentTP);
PrintFormat("[TRAILING] SL Sell #%d mis à jour : %.2f", ticket, candidateSL);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Calcul dynamique des lots selon 20% d'équité risquée |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double slPrice)
{
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double riskAmount = equity * (InpRiskPercent / 100.0);
double priceRisk = MathAbs(entryPrice - slPrice);
if(priceRisk <= 0) return 0.0;
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0 || tickValue <= 0) return 0.0;
double lossPerLot = (priceRisk / tickSize) * tickValue;
if(lossPerLot <= 0) return 0.0;
double lot = riskAmount / lossPerLot;
double stepVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lot = MathFloor(lot / stepVol) * stepVol;
if(lot < minVol) lot = minVol;
if(lot > maxVol) lot = maxVol;
return lot;
}