IFVG Indicator won't delete invalidated IFVG boxes

 

Hey erveryone hope you guys are doing well. 

I got an mlq5 code am trying to fix the deletion logic for invalidated logic but it is failing no matter waht i edit. Tried also chatgpt and deepseek but nothing fixes the issure. i have added candle lookbar, invalidation closes ,alert trigger deletes all of them but the broken zones stay solid.

#property indicator_chart_window
#property strict

#property indicator_buffers 0
#property indicator_plots   0

input int    LookbackBars      = 100;
input int    ATR_Period        = 14;
input double ATR_Multiplier    = 0.3;
input bool   EnableAlerts      = true; // Added alert toggle
input int    InvalidationLookback = 70;  // Bars to check for zone violations
input color  BullishColor      = clrGreen;
input color  BearishColor      = clrRed;

string logFileName = "Invalidated_iFVG_Zones.csv";

// Added arrays for alert tracking
string alertedBrokenIFVGs[];

// Added helper functions for alerts
bool StringArrayContains(const string &arr[], string value) {
    for(int i=0; i<ArraySize(arr); i++) {
        if(arr[i] == value) return true;
    }
    return false;
}

void StringArrayAddUnique(string &arr[], string value) {
    if(!StringArrayContains(arr, value)) {
        ArrayResize(arr, ArraySize(arr)+1);
        arr[ArraySize(arr)-1] = value;
    }
}

void ArrayRemove(string &arr[], int index) {
    if(index < 0 || index >= ArraySize(arr)) return;
    for(int i=index; i<ArraySize(arr)-1; i++) arr[i] = arr[i+1];
    ArrayResize(arr, ArraySize(arr)-1);
}

double CalculateATR(const double &high[], const double &low[], const double &close[], int current)
{
   if(current < ATR_Period)
      return 0.0;

   double sumTR = 0.0;
   for(int i = current - ATR_Period + 1; i <= current; i++)
   {
      double tr = MathMax(high[i] - low[i], MathMax(MathAbs(high[i] - close[i-1]), MathAbs(low[i] - close[i-1])));
      sumTR += tr;
   }
   return sumTR / ATR_Period;
}

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, -1, -1);
}

// ... [Keep all previous code identical until CleanInvalidatedZones function] ...

void CleanInvalidatedZones(const double &close[], const datetime &time[], int rates_total)
{
    #define DELETION_PRECISION 0.000001
    
    // Get current symbol precision
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    
    for(int idx = ObjectsTotal(0)-1; idx >= 0; idx--)
    {
        string name = ObjectName(0, idx);
        
        // Strict name filtering
        if(!StringStartsWith(name, "iFVGBull_") && !StringStartsWith(name, "iFVGBear_")) 
            continue;
            
        // Declare variables first
        double zone_top, zone_bottom;
        
        // Get zone properties with error checking (MQL5 syntax)
        if(!ObjectGetDouble(0, name, OBJPROP_PRICE, 0, zone_top)) continue;
        if(!ObjectGetDouble(0, name, OBJPROP_PRICE, 1, zone_bottom)) continue;
        
        datetime createTime = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
        
        // Robust age calculation
        int age = 2147483647;  // Use max int value directly instead of INT_MAX
        if(createTime > 0) {
            age = iBarShift(_Symbol, _Period, createTime, true);
            if(age == -1) age = rates_total;
        }
        
        bool isViolated = false;
        int check_bars = MathMin(InvalidationLookback, rates_total);
        
        // Precise price comparison
        for(int i = rates_total-1; i >= MathMax(0, rates_total - check_bars); i--)
        {
            double cl = NormalizeDouble(close[i], digits);
            
            if(StringStartsWith(name, "iFVGBull_")) 
            {
                double bottom = NormalizeDouble(zone_bottom - DELETION_PRECISION, digits);
                if(cl < bottom) {
                    isViolated = true;
                    break;
                }
            }
            else if(StringStartsWith(name, "iFVGBear_")) 
            {
                double top = NormalizeDouble(zone_top + DELETION_PRECISION, digits);
                if(cl > top) {
                    isViolated = true;
                    break;
                }
            }
        }
        
        // Strict deletion criteria
        bool isTooOld = (age > LookbackBars || age == -1);
        bool shouldDelete = isViolated || isTooOld;
        
        if(shouldDelete)
        {
            Print("Deleting zone: ", name, " | Violated: ", isViolated, " | Age: ", age);
            ObjectDelete(0, name);
            
            // Remove from alerted array if exists
            for(int a = ArraySize(alertedBrokenIFVGs)-1; a >= 0; a--) {
                if(alertedBrokenIFVGs[a] == name) {
                    ArrayRemove(alertedBrokenIFVGs, a);
                }
            }
            
            if(EnableAlerts && isViolated) {
                string alertType = StringSubstr(name, 0, 9); // "iFVGBull" or "iFVGBear"
                Alert(StringFormat("%s BROKEN | %s | %s | Price: %.5f",
                    alertType, Symbol(), EnumToString((ENUM_TIMEFRAMES)Period()), 
                    close[rates_total-1]));
            }
        }
    }
}

// Proper helper function implementation
bool StringStartsWith(const string str, const string substr)
{
    return (StringFind(str, substr, 0) == 0);
}









// ... [Keep all remaining code EXACTLY AS BEFORE] ...

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if (rates_total < ATR_Period + 3)
      return prev_calculated;

   int start = MathMax(ATR_Period, rates_total - LookbackBars - 3);
   bool is_initial_load = (prev_calculated == 0);
   CleanInvalidatedZones(close, time, rates_total);

   for (int i = start; i < rates_total - 2; i++)
   {
      double atr = CalculateATR(high, low, close, i) * ATR_Multiplier;
      double atr_points = atr / _Point;

      // Bullish FVG (when invalidated becomes iFVGBear)
      if (low[i + 2] > high[i])
      {
         double gap = low[i + 2] - high[i];
         if (gap / _Point >= atr_points)
         {
            bool invalidated = false;
            for (int j = i + 3; j < rates_total; j++)
            {
               if (close[j] < high[i])
               {
                  invalidated = true;
                  break;
               }
            }
            
            if (invalidated)
            {
               string iFVGName = "iFVGBear_" + TimeToString(time[i], TIME_DATE|TIME_MINUTES);
               if (ObjectFind(0, iFVGName) < 0)
               {
                  if (ObjectCreate(0, iFVGName, OBJ_RECTANGLE, 0, time[i+1], high[i], time[rates_total-1], low[i+2]))
                  {
                     ObjectSetInteger(0, iFVGName, OBJPROP_COLOR, BearishColor);
                     ObjectSetInteger(0, iFVGName, OBJPROP_STYLE, STYLE_SOLID);
                     ObjectSetInteger(0, iFVGName, OBJPROP_WIDTH, 1);
                     ObjectSetInteger(0, iFVGName, OBJPROP_BACK, true);
                     
                     // Added alert for new iFVG formation
                     if(EnableAlerts && !is_initial_load) {
                        Alert(StringFormat("BEARISH iFVG FORMED | %s | %s | Price: %.5f | At: %s",
                            Symbol(), EnumToString((ENUM_TIMEFRAMES)Period()),
                            close[rates_total-1], TimeToString(time[rates_total-1], TIME_DATE|TIME_MINUTES)));
                     }
                  }
               }
            }
         }
      }

      // Bearish FVG (when invalidated becomes iFVGBull)
      if (high[i + 2] < low[i])
      {
         double gap = low[i] - high[i + 2];
         if (gap / _Point >= atr_points)
         {
            bool invalidated = false;
            for (int j = i + 3; j < rates_total; j++)
            {
               if (close[j] > low[i])
               {
                  invalidated = true;
                  break;
               }
            }
            
            if (invalidated)
            {
               string iFVGName = "iFVGBull_" + TimeToString(time[i], TIME_DATE|TIME_MINUTES);
               if (ObjectFind(0, iFVGName) < 0)
               {
                  if (ObjectCreate(0, iFVGName, OBJ_RECTANGLE, 0, time[i+1], high[i+2], time[rates_total-1], low[i]))
                  {
                     ObjectSetInteger(0, iFVGName, OBJPROP_COLOR, BullishColor);
                     ObjectSetInteger(0, iFVGName, OBJPROP_STYLE, STYLE_SOLID);
                     ObjectSetInteger(0, iFVGName, OBJPROP_WIDTH, 1);
                     ObjectSetInteger(0, iFVGName, OBJPROP_BACK, true);
                     
                     // Added alert for new iFVG formation
                     if(EnableAlerts && !is_initial_load) {
                        Alert(StringFormat("BULLISH iFVG FORMED | %s | %s | Price: %.5f | At: %s",
                            Symbol(), EnumToString((ENUM_TIMEFRAMES)Period()),
                            close[rates_total-1], TimeToString(time[rates_total-1], TIME_DATE|TIME_MINUTES)));
                     }
                  }
               }
            }
         }
      }
   }

   return rates_total;
}


It keeps old broken levels that are supposed to be deleted once price close below or above them even by one pip. Not sure why kindly someone look into it. Its very close to completion. I wll appreciate it very much.