//+------------------------------------------------------------------+
//|  GoldLab Reversão (XAUUSD)
//|  Gerado pelo Gold Lab em 2026-10-01 21:34 UTC (geração 305)
//|
//|  Resultado medido no laboratório (ouro COMEX GC=F, H1, conta micro):
//|    treino          US$ 146.10 (+46.10%), queda máx. 9.9%, 29 operações
//|    validação       US$ 97.80 (-2.20%), queda máx. 21.4%, 7 operações
//|    fora da amostra US$ 85.83 (-14.17%), queda máx. 25.3%, 6 operações
//|
//|  Teste no Strategy Tester e em conta demo antes de qualquer conta real.
//|  Resultado passado não se repete no futuro.
//+------------------------------------------------------------------+
#property copyright "Gold Lab"
#property version   "1.0"
#property strict

#include <Trade/Trade.mqh>
CTrade trade;

input double RiskPercent  = 0.64;   // risco por operação, % do saldo
input double MaxRiskPct   = 10.0;         // nunca arriscar mais que isto
input long   MagicNumber  = 20260102;
input int    Slippage     = 20;
input int    BandPeriod   = 166;
input double BandStd      = 2.453;
input int    RsiPeriod    = 31;
input double RsiEdge      = 23.729;
input int    AtrPeriod    = 47;
input double StopAtr      = 3.601;
input double TargetAtr    = 3.937;

int atrHandle = INVALID_HANDLE;
int fastHandle = INVALID_HANDLE, slowHandle = INVALID_HANDLE, rsiHandle = INVALID_HANDLE, bandHandle = INVALID_HANDLE;
datetime lastBar = 0;

double Value(int handle, int buffer, int shift)
  {
   double tmp[];
   if(CopyBuffer(handle, buffer, shift, 1, tmp) <= 0)
      return(0.0);
   return(tmp[0]);
  }

//| Tamanho da posição a partir do risco e da distância do stop.
double LotsFor(double stopDistance)
  {
   double equity    = AccountInfoDouble(ACCOUNT_EQUITY);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickValue <= 0 || tickSize <= 0 || stopDistance <= 0)
      return(0.0);
   double lossPerLot = stopDistance / tickSize * tickValue;
   double lots       = equity * RiskPercent / 100.0 / lossPerLot;
   double step       = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double minLot     = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot     = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   lots = MathFloor(lots / step) * step;
   if(lots < minLot)
      lots = minLot;
   if(lots > maxLot)
      lots = maxLot;
   // Se nem o lote mínimo cabe no risco máximo, não opera.
   if(lots * lossPerLot > equity * MaxRiskPct / 100.0)
      return(0.0);
   return(lots);
  }

bool HasPosition()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket)
         && PositionGetString(POSITION_SYMBOL) == _Symbol
         && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
         return(true);
     }
   return(false);
  }

void Enter(int direction, double stopDistance, double targetDistance)
  {
   double lots = LotsFor(stopDistance);
   if(lots <= 0)
      return;
   double price = direction > 0 ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double sl    = direction > 0 ? price - stopDistance : price + stopDistance;
   double tp    = direction > 0 ? price + targetDistance : price - targetDistance;
   trade.SetDeviationInPoints(Slippage);
   trade.SetExpertMagicNumber(MagicNumber);
   if(direction > 0)
      trade.Buy(lots, _Symbol, price, sl, tp, "goldlab");
   else
      trade.Sell(lots, _Symbol, price, sl, tp, "goldlab");
  }

int OnInit()
  {
   atrHandle  = iATR(_Symbol, PERIOD_CURRENT, AtrPeriod);
   rsiHandle  = iRSI(_Symbol, PERIOD_CURRENT, RsiPeriod, PRICE_CLOSE);
   bandHandle = iBands(_Symbol, PERIOD_CURRENT, BandPeriod, 0, BandStd, PRICE_CLOSE);
   if(atrHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || bandHandle == INVALID_HANDLE)
      return(INIT_FAILED);
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
   datetime current = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(current == lastBar)
      return;
   lastBar = current;
   if(HasPosition())
      return;

   double atrValue = Value(atrHandle, 0, 1);
   if(atrValue <= 0)
      return;
   double upper = Value(bandHandle, 1, 1);
   double lower = Value(bandHandle, 2, 1);
   double rsi   = Value(rsiHandle, 0, 1);
   double close = iClose(_Symbol, PERIOD_CURRENT, 1);

   if(close < lower && rsi < RsiEdge)
      Enter(1, atrValue * StopAtr, atrValue * TargetAtr);
   else if(close > upper && rsi > 100.0 - RsiEdge)
      Enter(-1, atrValue * StopAtr, atrValue * TargetAtr);
  }
