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;
|
|
}
|