269 lines
10 KiB
MQL5
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;
| |||
}
|