//+------------------------------------------------------------------+
//|                                          RSIScalpingStrategy.mqh |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| RSI Scalping Strategy Data Structure                             |
//+------------------------------------------------------------------+
struct RSIScalpingData {
   string symbol;
   bool isInitialized;
   CTrade trade;
   int rsi_handle;
   double rsi_buffer[];
   double rsi_prev;
   double rsi_current;
   double rsi_two_bars_ago;
   bool position_open;
   ulong position_ticket;
   ENUM_POSITION_TYPE current_position_type;
   datetime last_bar_time;
   bool rsi_against_position;
   int bars_against_count;
};

string ErrorDescription(int errorCode)
{
   switch(errorCode)
   {
      case 4801: return "Symbol not found";
      case 4802: return "Symbol not selected";
      case 4803: return "Symbol not visible";
      case 4804: return "Symbol not available";
      case 4805: return "Cannot load indicator - insufficient history data";
      default: return "Unknown error " + IntegerToString(errorCode);
   }
}

bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period, 
                     ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
{
   data.symbol = symbol;
   data.isInitialized = false;
   
   // Check if symbol exists
   if(!SymbolSelect(symbol, true))
   {
      Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
      return false; // Return false but don't fail entire EA
   }
   
   // Wait a bit for symbol to be ready
   Sleep(100);
   
   // Try to create RSI indicator with retry logic (for insufficient history in backtesting)
   data.rsi_handle = INVALID_HANDLE;
   int retryCount = 0;
   int maxRetries = 5;
   
   while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
   {
      data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
      
      if(data.rsi_handle == INVALID_HANDLE)
      {
         int error = GetLastError();
         
         // Error 4805 = insufficient history - wait longer and retry
         if(error == 4805 && retryCount < maxRetries - 1)
         {
            Sleep(1000); // Wait 1 second for history to load
            retryCount++;
            continue;
         }
         
         Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
         return false; // Return false but don't fail entire EA
      }
   }
   
   if(data.rsi_handle == INVALID_HANDLE)
   {
      Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
      return false;
   }
   
   data.trade.SetExpertMagicNumber(MagicNumber);
   data.trade.SetDeviationInPoints(Slippage);
   data.trade.SetTypeFilling(ORDER_FILLING_FOK);
   
   ArraySetAsSeries(data.rsi_buffer, true);
   data.position_open = false;
   data.position_ticket = 0;
   data.rsi_against_position = false;
   data.bars_against_count = 0;
   data.isInitialized = true;
   
   Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
   return true;
}

void DeinitRSIScalping(RSIScalpingData& data)
{
   if(data.rsi_handle != INVALID_HANDLE)
      IndicatorRelease(data.rsi_handle);
}

bool UpdateRSI(RSIScalpingData& data)
{
   if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
      return false;
   
   data.rsi_current = data.rsi_buffer[0];
   data.rsi_prev = data.rsi_buffer[1];
   data.rsi_two_bars_ago = data.rsi_buffer[2];
   
   return true;
}

void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
                          double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
                          double RSI_Target_Sell, int BarsToWait)
{
   // Always check if position exists, even if tracking says it doesn't
   bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
   
   if(!positionExists && data.position_open)
   {
      // Position was closed externally, reset tracking
      data.position_open = false;
      data.position_ticket = 0;
      data.rsi_against_position = false;
      data.bars_against_count = 0;
      return;
   }
   
   if(!positionExists)
      return;
   
   // Update tracking if we have a position but tracking was lost
   if(!data.position_open && positionExists)
   {
      ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
      if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
      {
         data.position_ticket = ticket;
         data.position_open = true;
         data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      }
   }
   
   // Verify our tracked position still exists
   if(data.position_open && data.position_ticket > 0)
   {
      if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
      {
         // Try to find the position again
         ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
         if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
         {
            data.position_ticket = ticket;
            data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         }
         else
         {
            // Position doesn't exist, reset tracking
            data.position_open = false;
            data.position_ticket = 0;
            data.rsi_against_position = false;
            data.bars_against_count = 0;
            return;
         }
      }
      else
      {
         // Update position type in case it changed (shouldn't happen, but be safe)
         data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      }
   }
   
   if(data.current_position_type == POSITION_TYPE_BUY)
   {
      if(data.rsi_current < RSI_Oversold)
      {
         if(!data.rsi_against_position)
         {
            data.rsi_against_position = true;
            data.bars_against_count = 1;
         }
         else
         {
            data.bars_against_count++;
         }
         
         if(data.bars_against_count >= BarsToWait)
         {
            ClosePosition(data, MagicNumber);
            return;
         }
      }
      else
      {
         if(data.rsi_against_position)
         {
            data.rsi_against_position = false;
            data.bars_against_count = 0;
         }
         
         if(data.rsi_current >= RSI_Target_Buy)
         {
            ClosePosition(data, MagicNumber);
         }
      }
   }
   else if(data.current_position_type == POSITION_TYPE_SELL)
   {
      if(data.rsi_current > RSI_Overbought)
      {
         if(!data.rsi_against_position)
         {
            data.rsi_against_position = true;
            data.bars_against_count = 1;
         }
         else
         {
            data.bars_against_count++;
         }
         
         if(data.bars_against_count >= BarsToWait)
         {
            ClosePosition(data, MagicNumber);
            return;
         }
      }
      else
      {
         if(data.rsi_against_position)
         {
            data.rsi_against_position = false;
            data.bars_against_count = 0;
         }
         
         if(data.rsi_current <= RSI_Target_Sell)
         {
            ClosePosition(data, MagicNumber);
         }
      }
   }
}

void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
                      double RSI_Oversold, double RSI_Overbought, double LotSize)
{
   if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
   {
      OpenBuyPosition(data, MagicNumber, LotSize);
   }
   
   if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
   {
      OpenSellPosition(data, MagicNumber, LotSize);
   }
}

//+------------------------------------------------------------------+
//| Normalize Lot Size According to Symbol Properties                |
//+------------------------------------------------------------------+
double NormalizeLotSize(string symbol, double lotSize)
{
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   
   // Round to lot step
   if(lotStep > 0)
      lotSize = MathFloor(lotSize / lotStep) * lotStep;
   
   // Apply min/max constraints
   if(lotSize < minLot)
      lotSize = minLot;
   if(lotSize > maxLot)
      lotSize = maxLot;
   
   return lotSize;
}

void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
   if(PositionExistsByMagic(data.symbol, MagicNumber))
      return;
   
   // Normalize lot size according to symbol properties
   double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
   
   double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
   
   if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
   {
      ulong new_ticket = data.trade.ResultOrder();
      if(new_ticket > 0)
      {
         if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
         {
            data.position_ticket = new_ticket;
            data.position_open = true;
            data.current_position_type = POSITION_TYPE_BUY;
         }
      }
   }
}

void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
   if(PositionExistsByMagic(data.symbol, MagicNumber))
      return;
   
   // Normalize lot size according to symbol properties
   double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
   
   double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
   
   if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
   {
      ulong new_ticket = data.trade.ResultOrder();
      if(new_ticket > 0)
      {
         if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
         {
            data.position_ticket = new_ticket;
            data.position_open = true;
            data.current_position_type = POSITION_TYPE_SELL;
         }
      }
   }
}

void ClosePosition(RSIScalpingData& data, int MagicNumber)
{
   // First verify position still exists
   if(!PositionExistsByMagic(data.symbol, MagicNumber))
   {
      // Position doesn't exist, reset tracking
      data.position_open = false;
      data.position_ticket = 0;
      data.rsi_against_position = false;
      data.bars_against_count = 0;
      return;
   }
   
   // Try to close by ticket first (more reliable)
   bool closed = false;
   if(data.position_ticket > 0)
   {
      if(PositionSelectByTicket(data.position_ticket))
      {
         // Verify it's our position
         if(PositionGetString(POSITION_SYMBOL) == data.symbol && 
            PositionGetInteger(POSITION_MAGIC) == MagicNumber)
         {
            closed = data.trade.PositionClose(data.position_ticket);
            if(!closed)
            {
               Print("RSIScalping: Failed to close position by ticket ", data.position_ticket, 
                     " - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
            }
         }
      }
   }
   
   // If ticket method failed, try magic number method
   if(!closed)
   {
      closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
      if(!closed)
      {
         Print("RSIScalping: Failed to close position by magic number for '", data.symbol, 
               "' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
      }
   }
   
   // Verify position is actually closed
   if(closed)
   {
      // Wait a moment and verify
      Sleep(50);
      if(!PositionExistsByMagic(data.symbol, MagicNumber))
      {
         data.position_open = false;
         data.position_ticket = 0;
         data.rsi_against_position = false;
         data.bars_against_count = 0;
         Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
      }
      else
      {
         Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
         // Try one more time
         Sleep(100);
         if(PositionExistsByMagic(data.symbol, MagicNumber))
         {
            ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
         }
         // Reset tracking anyway to prevent getting stuck
         data.position_open = false;
         data.position_ticket = 0;
         data.rsi_against_position = false;
         data.bars_against_count = 0;
      }
   }
   else
   {
      // Close failed, but reset tracking to prevent getting stuck
      // The position might have been closed externally
      data.position_open = false;
      data.position_ticket = 0;
      data.rsi_against_position = false;
      data.bars_against_count = 0;
   }
}

void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
                       ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
                       double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
                       int BarsToWait, double LotSize, int MagicNumber)
{
   // Skip if not initialized (symbol not available)
   if(!data.isInitialized)
      return;
      
   data.symbol = symbol; // Update symbol in case it changed
   if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
      return;
      
   datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
   if(current_bar_time == data.last_bar_time)
      return;
      
   data.last_bar_time = current_bar_time;
   
   if(!UpdateRSI(data))
      return;
   
   CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
                        RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
   
   if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
   {
      CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
   }
}

//+------------------------------------------------------------------+
