//+------------------------------------------------------------------+
//|                                      AutoTradingMultiLookback.mq5|
//+------------------------------------------------------------------+
#property copyright "Enhanced Trading System"
#property version   "3.03"
#property strict

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

//--- inputs
input ENUM_TIMEFRAMES InpTimeframe        = PERIOD_H1;    // Timeframe to look back on
input int            BaseLookbackCandles  = 10;           // Base number of candles (will adjust dynamically)
input double         InpMultiplier        = 7.0;          // Max multiplier (integer part used, >=1)
input bool           IncludeCurrentCandle = false;        // Include current forming candle in calculations

input double         MinimalDistancePoints = 20;          // Minimal distance between lines, in points

// Trading inputs
input double         RiskPercent          = 2.0;          // Risk percentage of balance per trade

// Default SL/TP pips used if ATR based SL is unavailable
input int            DefaultStopLossPips   = 50;           // Default Stop Loss in pips (0 = no SL)
input int            DefaultTakeProfitPips = 100;          // Default Take Profit in pips (0 = no TP)

input int            MaxOpenTrades        = 3;            // Maximum open trades at once
input bool           EnableBuyTrades      = true;         // Enable buy trades
input bool           EnableSellTrades     = true;         // Enable sell trades

// Trade hours (server time)
input int            TradeStartHour       = 7;            // Trading session start hour (inclusive)
input int            TradeEndHour         = 20;           // Trading session end hour (exclusive)

// Optional color inputs
input color          ColorHighLines       = clrRed;
input color          ColorLowLines        = clrBlue;

#define PREFIX_HIGH "AutoTrade_High_"
#define PREFIX_LOW  "AutoTrade_Low_"
#define MAGIC_NUMBER 12345

//--- globals
datetime lastBarTime = 0;
double   multiplier  = 1.0;

int      DynamicLookbackCandles = 10; 
int      TradeOnlyMultiplier = 3;     

int      atr_handle = INVALID_HANDLE;
int      sma_handle_short = INVALID_HANDLE;
int      sma_handle_long = INVALID_HANDLE;

// Arrays to store line levels and their states
struct LineData
{
   double price;
   int multiplier;
   bool crossedAbove;  // For high lines: price crossed above
   bool crossedBelow;  // For low lines: price crossed below
};

LineData highLines[];
LineData lowLines[];

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   if(BaseLookbackCandles < 1)
   {
      Print("Error: BaseLookbackCandles must be >= 1");
      return(INIT_FAILED);
   }

   multiplier = InpMultiplier;
   if(multiplier < 1.0)
   {
      Print("Warning: InpMultiplier < 1, setting to 1");
      multiplier = 1.0;
   }

   if(RiskPercent < 0.1 || RiskPercent > 10)
   {
      Print("Error: RiskPercent must be between 0.1 and 10");
      return(INIT_FAILED);
   }

   atr_handle = iATR(_Symbol, InpTimeframe, 14);
   if(atr_handle == INVALID_HANDLE)
   {
      Print("Failed to create ATR indicator handle");
      return(INIT_FAILED);
   }

   sma_handle_short = iMA(_Symbol, InpTimeframe, 10, 0, MODE_SMA, PRICE_CLOSE);
   if(sma_handle_short == INVALID_HANDLE)
   {
      Print("Failed to create short SMA handle");
      return(INIT_FAILED);
   }
   sma_handle_long = iMA(_Symbol, InpTimeframe, 50, 0, MODE_SMA, PRICE_CLOSE);
   if(sma_handle_long == INVALID_HANDLE)
   {
      Print("Failed to create long SMA handle");
      return(INIT_FAILED);
   }

   trade.SetExpertMagicNumber(MAGIC_NUMBER);
   
   ClearLines();
   UpdateLines();

   Print("AutoTradingMultiLookback EA v3.03 initialized");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(atr_handle != INVALID_HANDLE)
   {
      IndicatorRelease(atr_handle);
      atr_handle = INVALID_HANDLE;
   }
   if(sma_handle_short != INVALID_HANDLE)
   {
      IndicatorRelease(sma_handle_short);
      sma_handle_short = INVALID_HANDLE;
   }
   if(sma_handle_long != INVALID_HANDLE)
   {
      IndicatorRelease(sma_handle_long);
      sma_handle_long = INVALID_HANDLE;
   }
   ClearLines();
}

//+------------------------------------------------------------------+
//| Calculate ATR value                                              |
//+------------------------------------------------------------------+
double CalculateATR()
{
   if(atr_handle == INVALID_HANDLE) return 0.0;

   double atr_buffer[];
   if(CopyBuffer(atr_handle,0,0,1,atr_buffer) <= 0)
   {
      Print("Failed to copy ATR buffer");
      return 0.0;
   }
   return atr_buffer[0];
}

//+------------------------------------------------------------------+
//| Get SMA value from handle                                        |
//+------------------------------------------------------------------+
double GetSMA(int handle)
{
   if(handle == INVALID_HANDLE) return 0.0;

   double buffer[];
   if(CopyBuffer(handle, 0, 0, 1, buffer) <= 0)
   {
      Print("Failed to copy SMA buffer");
      return 0.0;
   }
   return buffer[0];
}

//+------------------------------------------------------------------+
//| Adjust lookback candles and multiplier dynamically based on ATR |
//+------------------------------------------------------------------+
void AdjustParametersByVolatility()
{
   double atr = CalculateATR();
   if(atr <= 0)
   {
      DynamicLookbackCandles = BaseLookbackCandles;
      TradeOnlyMultiplier = (int)MathMin(multiplier, 3);
      return;
   }

   double lowVolThreshold = 0.0005;
   double highVolThreshold = 0.0012;

   if(atr < lowVolThreshold)
   {
      DynamicLookbackCandles = MathMax(3, BaseLookbackCandles / 2);
      TradeOnlyMultiplier = 1;
   }
   else if(atr > highVolThreshold)
   {
      DynamicLookbackCandles = BaseLookbackCandles * 2;
      TradeOnlyMultiplier = (int)MathMin(multiplier, 5);
   }
   else
   {
      DynamicLookbackCandles = BaseLookbackCandles;
      TradeOnlyMultiplier = (int)MathMin(multiplier, 3);
   }

   PrintFormat("Volatility-based params: ATR=%.5f, Lookback=%d, Multiplier=%d", atr, DynamicLookbackCandles, TradeOnlyMultiplier);
}

//+------------------------------------------------------------------+
//| Adaptive StopLoss pips                                           |
//+------------------------------------------------------------------+
int GetAdaptiveStopLossPips()
{
   double atr = CalculateATR();
   if(atr <= 0)
      return(DefaultStopLossPips);

   double slPips = (atr / _Point) * 4.0;
   slPips = MathMax(slPips, 10);
   slPips = MathMin(slPips, 150);

   return (int)slPips;
}

//+------------------------------------------------------------------+
//| Adaptive TakeProfit pips based on StopLoss                      |
//+------------------------------------------------------------------+
int GetAdaptiveTakeProfitPips(int slPips)
{
   int tpPips = (int)(slPips * 2.5);
   tpPips = MathMax(tpPips, 20);
   tpPips = MathMin(tpPips, 400);
   return tpPips;
}

//+------------------------------------------------------------------+
//| Trend filters with SMA                                           |
//+------------------------------------------------------------------+
bool IsTrendingUp()
{
   double smaShort = GetSMA(sma_handle_short);
   double smaLong  = GetSMA(sma_handle_long);
   return (smaShort > smaLong);
}

bool IsTrendingDown()
{
   double smaShort = GetSMA(sma_handle_short);
   double smaLong  = GetSMA(sma_handle_long);
   return (smaShort < smaLong);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   MqlDateTime currentTime;
   TimeCurrent(currentTime);
   if(currentTime.hour < TradeStartHour || currentTime.hour >= TradeEndHour)
      return; // Outside trading hours

   datetime currentBarTime = iTime(_Symbol, InpTimeframe, 0);
   if(currentBarTime != lastBarTime)
   {
      lastBarTime = currentBarTime;

      AdjustParametersByVolatility();

      UpdateLines();
   }

   CheckTradingSignals();
}

//+------------------------------------------------------------------+
//| Update or create lines                                          |
//+------------------------------------------------------------------+
void UpdateLines()
{
   int maxMult = (int)MathFloor(multiplier);
   if(maxMult < 1) maxMult = 1;

   int startPos = IncludeCurrentCandle ? 0 : 1;

   ClearLines();
   ArrayResize(highLines, 0);
   ArrayResize(lowLines, 0);

   double filteredHighsPrices[];
   int    filteredHighsMultipliers[];
   double filteredLowsPrices[];
   int    filteredLowsMultipliers[];

   for(int m = 1; m <= maxMult; m++)
   {
      int barsToLookBack = DynamicLookbackCandles * m;

      MqlRates rates[];
      int copied = CopyRates(_Symbol, InpTimeframe, startPos, barsToLookBack, rates);

      if(copied <= 0)
      {
         PrintFormat("CopyRates failed for %d bars on timeframe %s", barsToLookBack, EnumToString(InpTimeframe));
         continue;
      }

      double highest = -DBL_MAX;
      double lowest = DBL_MAX;

      for(int i = 0; i < copied; i++)
      {
         if(rates[i].high > highest) highest = rates[i].high;
         if(rates[i].low < lowest) lowest = rates[i].low;
      }

      ProcessHighLine(highest, m, filteredHighsPrices, filteredHighsMultipliers);
      ProcessLowLine(lowest, m, filteredLowsPrices, filteredLowsMultipliers);
   }
}

//+------------------------------------------------------------------+
//| Process high line                                               |
//+------------------------------------------------------------------+
void ProcessHighLine(double highest, int mult, double &filteredHighsPrices[], int &filteredHighsMultipliers[])
{
   bool foundClose = false;
   int indexClose = -1;

   for(int k = 0; k < ArraySize(filteredHighsPrices); k++)
   {
      double distPoints = MathAbs(highest - filteredHighsPrices[k]) / _Point;
      if(distPoints < MinimalDistancePoints)
      {
         foundClose = true;
         indexClose = k;
         break;
      }
   }

   string name = PREFIX_HIGH + IntegerToString(mult);

   if(!foundClose)
   {
      ArrayResize(filteredHighsPrices, ArraySize(filteredHighsPrices)+1);
      ArrayResize(filteredHighsMultipliers, ArraySize(filteredHighsMultipliers)+1);
      filteredHighsPrices[ArraySize(filteredHighsPrices)-1] = highest;
      filteredHighsMultipliers[ArraySize(filteredHighsMultipliers)-1] = mult;

      ArrayResize(highLines, ArraySize(highLines)+1);
      highLines[ArraySize(highLines)-1].price = highest;
      highLines[ArraySize(highLines)-1].multiplier = mult;
      highLines[ArraySize(highLines)-1].crossedAbove = false;

      DrawOrUpdateLine(name, highest, ColorHighLines);
   }
   else if(highest > filteredHighsPrices[indexClose])
   {
      filteredHighsPrices[indexClose] = highest;

      int replacedMultiplier = filteredHighsMultipliers[indexClose];
      string replacedName = PREFIX_HIGH + IntegerToString(replacedMultiplier);

      for(int k=0; k<ArraySize(highLines); k++)
      {
         if(highLines[k].multiplier == replacedMultiplier)
         {
            highLines[k].price = highest;
            highLines[k].crossedAbove = false;
            break;
         }
      }

      DrawOrUpdateLine(replacedName, highest, ColorHighLines);
   }
}

//+------------------------------------------------------------------+
//| Process low line                                              |
//+------------------------------------------------------------------+
void ProcessLowLine(double lowest, int mult, double &filteredLowsPrices[], int &filteredLowsMultipliers[])
{
   bool foundClose = false;
   int indexClose = -1;

   for(int k = 0; k < ArraySize(filteredLowsPrices); k++)
   {
      double distPoints = MathAbs(lowest - filteredLowsPrices[k]) / _Point;
      if(distPoints < MinimalDistancePoints)
      {
         foundClose = true;
         indexClose = k;
         break;
      }
   }

   string name = PREFIX_LOW + IntegerToString(mult);

   if(!foundClose)
   {
      ArrayResize(filteredLowsPrices, ArraySize(filteredLowsPrices)+1);
      ArrayResize(filteredLowsMultipliers, ArraySize(filteredLowsMultipliers)+1);
      filteredLowsPrices[ArraySize(filteredLowsPrices)-1] = lowest;
      filteredLowsMultipliers[ArraySize(filteredLowsMultipliers)-1] = mult;

      ArrayResize(lowLines, ArraySize(lowLines)+1);
      lowLines[ArraySize(lowLines)-1].price = lowest;
      lowLines[ArraySize(lowLines)-1].multiplier = mult;
      lowLines[ArraySize(lowLines)-1].crossedBelow = false;

      DrawOrUpdateLine(name, lowest, ColorLowLines);
   }
   else if(lowest < filteredLowsPrices[indexClose])
   {
      filteredLowsPrices[indexClose] = lowest;

      int replacedMultiplier = filteredLowsMultipliers[indexClose];
      string replacedName = PREFIX_LOW + IntegerToString(replacedMultiplier);

      for(int k=0; k<ArraySize(lowLines); k++)
      {
         if(lowLines[k].multiplier == replacedMultiplier)
         {
            lowLines[k].price = lowest;
            lowLines[k].crossedBelow = false;
            break;
         }
      }

      DrawOrUpdateLine(replacedName, lowest, ColorLowLines);
   }
}

//+------------------------------------------------------------------+
//| Check for trading signals                                       |
//+------------------------------------------------------------------+
void CheckTradingSignals()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   MqlRates prevCandle[];
   if(CopyRates(_Symbol, InpTimeframe, 1, 1, prevCandle) <= 0)
   {
      Print("Failed to get previous candle rates");
      return;
   }
   double prevClose = prevCandle[0].close;

   if(EnableBuyTrades && IsTrendingUp())
   {
      for(int j = 0; j < ArraySize(lowLines); j++)
      {
         if(lowLines[j].multiplier != TradeOnlyMultiplier) continue;

         double linePrice = lowLines[j].price;

         if(!lowLines[j].crossedBelow && currentAsk < linePrice)
         {
            lowLines[j].crossedBelow = true;
            PrintFormat("Tick: Price crossed BELOW low line multiplier %d at %.5f", lowLines[j].multiplier, linePrice);
         }

         if(lowLines[j].crossedBelow && currentAsk > linePrice)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenBuyTrade(lowLines[j].multiplier, linePrice);
            }
            lowLines[j].crossedBelow = false;
         }

         if(lowLines[j].crossedBelow && prevClose > linePrice)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenBuyTrade(lowLines[j].multiplier, linePrice);
            }
            lowLines[j].crossedBelow = false;
         }
      }
   }

   if(EnableSellTrades && IsTrendingDown())
   {
      for(int j = 0; j < ArraySize(highLines); j++)
      {
         if(highLines[j].multiplier != TradeOnlyMultiplier) continue;

         double linePrice = highLines[j].price;

         if(!highLines[j].crossedAbove && currentBid > linePrice)
         {
            highLines[j].crossedAbove = true;
            PrintFormat("Tick: Price crossed ABOVE high line multiplier %d at %.5f", highLines[j].multiplier, linePrice);
         }

         if(highLines[j].crossedAbove && currentBid < linePrice)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenSellTrade(highLines[j].multiplier, linePrice);
            }
            highLines[j].crossedAbove = false;
         }

         if(highLines[j].crossedAbove && prevClose < linePrice)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenSellTrade(highLines[j].multiplier, linePrice);
            }
            highLines[j].crossedAbove = false;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Open sell trade                                                 |
//+------------------------------------------------------------------+
void OpenSellTrade(int multiplier, double triggerPrice)
{
   double lotSize = CalculateLotSize();
   if(lotSize <= 0) return;

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double sl=0, tp=0;

   double pipMultiplier = (_Digits == 5 || _Digits == 3) ? 10.0 : 1.0;

   int slPips = GetAdaptiveStopLossPips();
   int tpPips = GetAdaptiveTakeProfitPips(slPips);

   sl = price + (slPips * _Point * pipMultiplier);
   tp = price - (tpPips * _Point * pipMultiplier);

   string comment = StringFormat("SELL M%d at %.5f", multiplier, triggerPrice);

   if(trade.Sell(lotSize, _Symbol, price, sl, tp, comment))
   {
      PrintFormat("SELL order opened: Lot=%.2f, Price=%.5f, SL=%.5f, TP=%.5f, Multiplier=%d",
                  lotSize, price, sl, tp, multiplier);
   }
   else
   {
      PrintFormat("Failed to open SELL order. Error: %d", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Open buy trade                                                  |
//+------------------------------------------------------------------+
void OpenBuyTrade(int multiplier, double triggerPrice)
{
   double lotSize = CalculateLotSize();
   if(lotSize <= 0) return;

   double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double sl=0, tp=0;

   double pipMultiplier = (_Digits == 5 || _Digits == 3) ? 10.0 : 1.0;

   int slPips = GetAdaptiveStopLossPips();
   int tpPips = GetAdaptiveTakeProfitPips(slPips);

   sl = price - (slPips * _Point * pipMultiplier);
   tp = price + (tpPips * _Point * pipMultiplier);

   string comment = StringFormat("BUY M%d at %.5f", multiplier, triggerPrice);

   if(trade.Buy(lotSize, _Symbol, price, sl, tp, comment))
   {
      PrintFormat("BUY order opened: Lot=%.2f, Price=%.5f, SL=%.5f, TP=%.5f, Multiplier=%d",
                  lotSize, price, sl, tp, multiplier);
   }
   else
   {
      PrintFormat("Failed to open BUY order. Error: %d", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Calculate lot size based on risk percentage                      |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmount = balance * (RiskPercent / 100.0);

   double pipMultiplier = (_Digits == 5 || _Digits == 3) ? 10.0 : 1.0;

   int slPips = GetAdaptiveStopLossPips();

   if(slPips <= 0)
   {
      double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
      double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
      double pointValue = tickValue * (_Point / tickSize);
      double lotSize = riskAmount / (50 * pointValue * pipMultiplier);
      return NormalizeLot(lotSize);
   }

   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double pointValue = tickValue * (_Point / tickSize);

   double lotSize = riskAmount / (slPips * pointValue * pipMultiplier);

   return NormalizeLot(lotSize);
}

//+------------------------------------------------------------------+
//| Normalize lot size                                               |
//+------------------------------------------------------------------+
double NormalizeLot(double lot)
{
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   if(lot < minLot) return minLot;
   if(lot > maxLot) return maxLot;

   return MathFloor(lot / lotStep) * lotStep;
}

//+------------------------------------------------------------------+
//| Get count of open trades                                         |
//+------------------------------------------------------------------+
int GetOpenTradesCount()
{
   int count = 0;
   int totalPositions = PositionsTotal();

   for(int i = 0; i < totalPositions; i++)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;

      if(PositionSelectByTicket(ticket))
      {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
            PositionGetInteger(POSITION_MAGIC) == MAGIC_NUMBER)
         {
            count++;
         }
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| Draw or update horizontal line                                   |
//+------------------------------------------------------------------+
void DrawOrUpdateLine(string name, double price, color clr)
{
   if(ObjectFind(0, name) == -1)
   {
      if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
      {
         Print("Failed to create line: " + name);
         return;
      }
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
   }
   else
   {
      ObjectSetDouble(0, name, OBJPROP_PRICE, price);
   }
}

//+------------------------------------------------------------------+
//| Clear lines with defined prefixes                                |
//+------------------------------------------------------------------+
void ClearLines()
{
   int total = ObjectsTotal(0);
   for(int i=total-1; i>=0; i--)
   {
      string name = ObjectName(0, i);
      if(StringFind(name, PREFIX_HIGH) == 0 || StringFind(name, PREFIX_LOW) == 0)
         ObjectDelete(0, name);
   }
}
//+------------------------------------------------------------------+