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

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

//--- inputs
input ENUM_TIMEFRAMES InpTimeframe        = PERIOD_H1;    // Timeframe to look back on
input int            InpLookbackCandles   = 10;           // Base number of candles (>=1)
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
input int            StopLossPips         = 50;           // Stop Loss in pips (0 = no SL)
input int            TakeProfitPips       = 100;          // Take Profit in pips (0 = no TP)
input int            MaxOpenTrades        = 3;            // Maximum open trades at once
input bool           EnableBuyTrades      = true;         // Enable buy trades (price cross low and close above)
input bool           EnableSellTrades     = true;         // Enable sell trades (price cross high and close below)
input int            MinMultiplier        = 1;            // Minimum multiplier to trade
input int            MaxMultiplier        = 7;            // Maximum multiplier to trade

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

// 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 function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(InpLookbackCandles < 1)
   {
      Print("Error: InpLookbackCandles 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 || RiskPercent > 10)
   {
      Print("Error: RiskPercent must be between 0.1 and 10");
      return(INIT_FAILED);
   }

   // Initialize trade object
   trade.SetExpertMagicNumber(MAGIC_NUMBER);
   
   ClearLines();
   UpdateLines();

   Print("AutoTradingMultiLookback EA initialized");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ClearLines();
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Get time of the latest completed bar on the input timeframe
   datetime currentBarTime = iTime(_Symbol, InpTimeframe, 0);
   if(currentBarTime != lastBarTime)
   {
      lastBarTime = currentBarTime;
      UpdateLines();
   }

   // Check for trading signals on every tick
   CheckTradingSignals();
}

//+------------------------------------------------------------------+
//| Core function to draw or update all lines                       |
//+------------------------------------------------------------------+
void UpdateLines()
{
   int maxMult = (int)MathFloor(multiplier);
   if(maxMult < 1) maxMult = 1;

   int startPos = IncludeCurrentCandle ? 0 : 1;

   // Clear previous lines and reset arrays
   ClearLines();
   ArrayResize(highLines, 0);
   ArrayResize(lowLines, 0);

   // Temporary arrays for filtering
   double filteredHighsPrices[];
   int    filteredHighsMultipliers[];
   double filteredLowsPrices[];
   int    filteredLowsMultipliers[];

   for(int m = 1; m <= maxMult; m++)
   {
      int barsToLookBack = InpLookbackCandles * 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;
      }

      // Filter and add high lines
      ProcessHighLine(highest, m, filteredHighsPrices, filteredHighsMultipliers);
      
      // Filter and add low lines
      ProcessLowLine(lowest, m, filteredLowsPrices, filteredLowsMultipliers);
   }
}

//+------------------------------------------------------------------+
//| Process high line with filtering                                 |
//+------------------------------------------------------------------+
void ProcessHighLine(double highest, int m, double &filteredHighsPrices[], int &filteredHighsMultipliers[])
{
   bool highCloseFound = false;
   int  highCloseIndex = -1;

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

   string highLineName = PREFIX_HIGH + IntegerToString(m);

   if(!highCloseFound)
   {
      // Add new high line
      ArrayResize(filteredHighsPrices, ArraySize(filteredHighsPrices) + 1);
      ArrayResize(filteredHighsMultipliers, ArraySize(filteredHighsMultipliers) + 1);
      filteredHighsPrices[ArraySize(filteredHighsPrices) - 1] = highest;
      filteredHighsMultipliers[ArraySize(filteredHighsMultipliers) - 1] = m;

      // Add to trading array
      ArrayResize(highLines, ArraySize(highLines) + 1);
      highLines[ArraySize(highLines) - 1].price = highest;
      highLines[ArraySize(highLines) - 1].multiplier = m;
      highLines[ArraySize(highLines) - 1].crossedAbove = false;

      DrawOrUpdateLine(highLineName, highest, ColorHighLines);
   }
   else if(highest > filteredHighsPrices[highCloseIndex])
   {
      // Replace with higher value
      filteredHighsPrices[highCloseIndex] = highest;
      int replacedMultiplier = filteredHighsMultipliers[highCloseIndex];
      string replacedLineName = PREFIX_HIGH + IntegerToString(replacedMultiplier);
      
      // Update in trading array
      for(int i = 0; i < ArraySize(highLines); i++)
      {
         if(highLines[i].multiplier == replacedMultiplier)
         {
            highLines[i].price = highest;
            highLines[i].crossedAbove = false;
            break;
         }
      }
      
      DrawOrUpdateLine(replacedLineName, highest, ColorHighLines);
   }
}

//+------------------------------------------------------------------+
//| Process low line with filtering                                  |
//+------------------------------------------------------------------+
void ProcessLowLine(double lowest, int m, double &filteredLowsPrices[], int &filteredLowsMultipliers[])
{
   bool lowCloseFound = false;
   int  lowCloseIndex = -1;

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

   string lowLineName = PREFIX_LOW + IntegerToString(m);

   if(!lowCloseFound)
   {
      // Add new low line
      ArrayResize(filteredLowsPrices, ArraySize(filteredLowsPrices) + 1);
      ArrayResize(filteredLowsMultipliers, ArraySize(filteredLowsMultipliers) + 1);
      filteredLowsPrices[ArraySize(filteredLowsPrices) - 1] = lowest;
      filteredLowsMultipliers[ArraySize(filteredLowsMultipliers) - 1] = m;

      // Add to trading array
      ArrayResize(lowLines, ArraySize(lowLines) + 1);
      lowLines[ArraySize(lowLines) - 1].price = lowest;
      lowLines[ArraySize(lowLines) - 1].multiplier = m;
      lowLines[ArraySize(lowLines) - 1].crossedBelow = false;

      DrawOrUpdateLine(lowLineName, lowest, ColorLowLines);
   }
   else if(lowest < filteredLowsPrices[lowCloseIndex])
   {
      // Replace with lower value
      filteredLowsPrices[lowCloseIndex] = lowest;
      int replacedMultiplier = filteredLowsMultipliers[lowCloseIndex];
      string replacedLineName = PREFIX_LOW + IntegerToString(replacedMultiplier);
      
      // Update in trading array
      for(int i = 0; i < ArraySize(lowLines); i++)
      {
         if(lowLines[i].multiplier == replacedMultiplier)
         {
            lowLines[i].price = lowest;
            lowLines[i].crossedBelow = false;
            break;
         }
      }
      
      DrawOrUpdateLine(replacedLineName, lowest, ColorLowLines);
   }
}

//+------------------------------------------------------------------+
//| Check for trading signals                                        |
//+------------------------------------------------------------------+
void CheckTradingSignals()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   // Check sell signals (price cross high and close below)
   if(EnableSellTrades)
   {
      for(int i = 0; i < ArraySize(highLines); i++)
      {
         // Check if multiplier is within trading range
         if(highLines[i].multiplier < MinMultiplier || highLines[i].multiplier > MaxMultiplier)
            continue;
            
         // Check if price crossed above the high line
         if(!highLines[i].crossedAbove && currentBid > highLines[i].price)
         {
            highLines[i].crossedAbove = true;
            PrintFormat("Price crossed ABOVE high line multiplier %d at %.5f", highLines[i].multiplier, highLines[i].price);
         }
         
         // Check if price closed below after crossing above (sell signal)
         if(highLines[i].crossedAbove && currentBid < highLines[i].price)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenSellTrade(highLines[i].multiplier, highLines[i].price);
            }
            highLines[i].crossedAbove = false; // Reset for next signal
         }
      }
   }
   
   // Check buy signals (price cross low and close above)
   if(EnableBuyTrades)
   {
      for(int i = 0; i < ArraySize(lowLines); i++)
      {
         // Check if multiplier is within trading range
         if(lowLines[i].multiplier < MinMultiplier || lowLines[i].multiplier > MaxMultiplier)
            continue;
            
         // Check if price crossed below the low line
         if(!lowLines[i].crossedBelow && currentAsk < lowLines[i].price)
         {
            lowLines[i].crossedBelow = true;
            PrintFormat("Price crossed BELOW low line multiplier %d at %.5f", lowLines[i].multiplier, lowLines[i].price);
         }
         
         // Check if price closed above after crossing below (buy signal)
         if(lowLines[i].crossedBelow && currentAsk > lowLines[i].price)
         {
            if(GetOpenTradesCount() < MaxOpenTrades)
            {
               OpenBuyTrade(lowLines[i].multiplier, lowLines[i].price);
            }
            lowLines[i].crossedBelow = false; // Reset for next signal
         }
      }
   }
}

//+------------------------------------------------------------------+
//| 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;
   
   // Calculate Stop Loss
   if(StopLossPips > 0)
   {
      sl = price + (StopLossPips * _Point * 10); // For 5-digit brokers
   }
   
   // Calculate Take Profit
   if(TakeProfitPips > 0)
   {
      tp = price - (TakeProfitPips * _Point * 10); // For 5-digit brokers
   }
   
   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;
   
   // Calculate Stop Loss
   if(StopLossPips > 0)
   {
      sl = price - (StopLossPips * _Point * 10); // For 5-digit brokers
   }
   
   // Calculate Take Profit
   if(TakeProfitPips > 0)
   {
      tp = price + (TakeProfitPips * _Point * 10); // For 5-digit brokers
   }
   
   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);
   
   if(StopLossPips <= 0)
   {
      // If no stop loss, use fixed risk amount with default pip value
      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 * 10); // Assume 50 pips risk
      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 / (StopLossPips * pointValue * 10);
   
   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;
   for(int i = 0; i < PositionsTotal(); i++)
   {
      if(PositionSelectByIndex(i))
      {
         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);
   }
}
//+------------------------------------------------------------------+