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. 

 
Ismail Lemin:

I reviewed your code and the main issue seems to be how you're accessing the rectangle's price levels.

You're using OBJPROP_PRICE, but for OBJ_RECTANGLE objects you should use OBJPROP_PRICE1 and OBJPROP_PRICE2. Since those calls fail, the code just skips deletion. If you switch to the correct properties, it should start working.

Also, be cautious with how you're naming the rectangles. Using TimeToString(...) can create inconsistent names that are harder to match later. It's safer to use something like IntegerToString((int)time[i]) for a consistent and unique name.

 
Miguel Angel Vico Alba #:

I reviewed your code and the main issue seems to be how you're accessing the rectangle's price levels.

You're using OBJPROP_PRICE, but for OBJ_RECTANGLE objects you should use OBJPROP_PRICE1 and OBJPROP_PRICE2. Since those calls fail, the code just skips deletion. If you switch to the correct properties, it should start working.

Also, be cautious with how you're naming the rectangles. Using TimeToString(...) can create inconsistent names that are harder to match later. It's safer to use something like IntegerToString((int)time[i]) for a consistent and unique name.

Hello Miguel hope you are doing well. I did try to use the OBJPROP_PRICE1 and 2 but it gives errors . can i share the same indicator that has IIFVG arrow whcih still wont delete once invalidated. If they can delete its when i shift to drawing IFVG boxes instead of arrow. Kindly help please will appreciate it so much. The fvg part is very okay drawing hidding and deleting is smooth. Just the issue with IFVG arrows that wont delete. 

#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.35;
input color  BullishColor       = clrGreen;
input color  BearishColor       = clrRed;
input color  iBullishColor      = clrLime;     // Color for bullish iFVG arrows
input color  iBearishColor      = clrCrimson;  // Color for bearish iFVG arrows

input int    ArrowCodeBullish   = 233;
input int    ArrowCodeBearish   = 234;
input int    ArrowSize          = 1;
input int    ArrowOffsetPoints  = 20; 
input bool   ShowFVGArrows      = false;  // Toggle to show FVG arrows

struct FVG {
    int type;
    int startBar;
    int endBar;
    double highVal;
    double lowVal;
    double atr;
};

double CalculateATR(const double &high[], const double &low[], const double &close[], int rates_total, 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) {
    int total = ObjectsTotal(0, -1, -1);
    for (int j = total - 1; j >= 0; j--) {
        string name = ObjectName(0, j);
        if (StringFind(name, "BullFVG_") == 0 || StringFind(name, "BearFVG_") == 0 ||
            StringFind(name, "iBullFVG_") == 0 || StringFind(name, "iBearFVG_") == 0)
            ObjectDelete(0, name);
    }
}

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);
    FVG fvgs[];
    int fvgCount = 0;

    // Detect FVGs
    for(int i = start; i < rates_total - 3; i++) 
    {
        double atrValue = CalculateATR(high, low, close, rates_total, i);
        double atr = atrValue * ATR_Multiplier;

        // Bullish FVG detection
        if(low[i+2] > high[i] && ((low[i+2] - high[i])/_Point >= atr/_Point)) 
        {
            FVG fvg = {1, i, i+2, high[i], low[i+2], atr};
            ArrayResize(fvgs, fvgCount+1);
            fvgs[fvgCount++] = fvg;
        }
        // Bearish FVG detection
        else if(high[i+2] < low[i] && ((low[i] - high[i+2])/_Point >= atr/_Point)) 
        {
            FVG fvg = {-1, i, i+2, high[i+2], low[i], atr};
            ArrayResize(fvgs, fvgCount+1);
            fvgs[fvgCount++] = fvg;
        }
    }

    // Remove existing FVG arrows
    int total = ObjectsTotal(0, -1, -1);
    for (int j = total - 1; j >= 0; j--) {
        string name = ObjectName(0, j);
        if (StringFind(name, "BullFVG_") == 0 || StringFind(name, "BearFVG_") == 0)
            ObjectDelete(0, name);
    }

    // Draw current valid FVGs
    if (ShowFVGArrows) {
        for (int m = 0; m < fvgCount; m++) {
            FVG fvg = fvgs[m];
           bool broken = false;
for (int i = fvg.endBar + 1; i < rates_total - 1; i++) {
    if ((fvg.type == 1 && close[i] < fvg.lowVal) ||
        (fvg.type == -1 && close[i] > fvg.highVal)) {
        broken = true;
        break;
    }
}
if (broken)
    continue;


            string name = (fvg.type == 1 ? "BullFVG_" : "BearFVG_") + IntegerToString(fvg.startBar);
            double price = (fvg.type == 1)
                           ? (fvg.lowVal - ArrowOffsetPoints * _Point)
                           : (fvg.highVal + ArrowOffsetPoints * _Point);

            if (ObjectCreate(0, name, OBJ_ARROW, 0, time[fvg.endBar], price)) {
                ObjectSetInteger(0, name, OBJPROP_ARROWCODE, fvg.type == 1 ? ArrowCodeBullish : ArrowCodeBearish);
                ObjectSetInteger(0, name, OBJPROP_COLOR, fvg.type == 1 ? BullishColor : BearishColor);
                ObjectSetInteger(0, name, OBJPROP_WIDTH, ArrowSize);
            }
        }
    }

    // Detect and draw iFVGs
    for (int m = 0; m < fvgCount; m++) {
        FVG fvg = fvgs[m];
        string name = (fvg.type == -1 ? "iBullFVG_" : "iBearFVG_") + IntegerToString(fvg.startBar);

        if (ObjectFind(0, name) != -1) continue;

        double zoneTop = (fvg.type == 1) ? fvg.highVal : fvg.lowVal;
        double zoneBottom = (fvg.type == -1) ? fvg.lowVal : fvg.highVal;

        for (int i = fvg.endBar + 1; i < rates_total - 1; i++) {
            bool isInvalidated = false;
            double arrowPrice = 0;

            if (fvg.type == -1 && close[i] > zoneTop) {
                arrowPrice = low[i] - ArrowOffsetPoints * _Point;
                isInvalidated = true;
            }
            else if (fvg.type == 1 && close[i] < zoneBottom) {
                arrowPrice = high[i] + ArrowOffsetPoints * _Point;
                isInvalidated = true;
            }

            if (isInvalidated) {
                ObjectCreate(0, name, OBJ_ARROW, 0, time[i], arrowPrice);
                ObjectSetInteger(0, name, OBJPROP_ARROWCODE, (fvg.type == -1) ? ArrowCodeBullish : ArrowCodeBearish);
                ObjectSetInteger(0, name, OBJPROP_COLOR, (fvg.type == -1) ? iBullishColor : iBearishColor);
                ObjectSetInteger(0, name, OBJPROP_WIDTH, ArrowSize);
                break;
            }
        }
    }

// Invalidate iFVGs if price closes beyond the original FVG zone
for (int j = ObjectsTotal(0, -1, -1) - 1; j >= 0; j--) {
    string name = ObjectName(0, j);

    bool is_iBull = StringFind(name, "iBullFVG_") == 0;
    bool is_iBear = StringFind(name, "iBearFVG_") == 0;

    if (!is_iBull && !is_iBear)
        continue;

    int startBar = (int)StringToInteger(StringSubstr(name, StringLen(name) - StringLen(IntegerToString(LookbackBars))));

    // Locate original FVG
    FVG matched;
    bool found = false;
    for (int i = 0; i < fvgCount; i++) {
        if (fvgs[i].startBar == startBar) {
            matched = fvgs[i];
            found = true;
            break;
        }
    }
    if (!found) continue;

    double top = matched.highVal;
    double bottom = matched.lowVal;

    // Check candles after the original FVG’s end bar
    for (int i = matched.endBar + 1; i < rates_total; i++) {
        if ((is_iBull && close[i] < bottom) || (is_iBear && close[i] > top)) {
            ObjectDelete(0, name);
            break;
        }
    }
}

    return rates_total;
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
    if (id == CHARTEVENT_OBJECT_CLICK) {
        if (StringFind(sparam, "BullFVG_") == 0 || StringFind(sparam, "BearFVG_") == 0) {
            MessageBox("To change indicator settings:\nRight-click on the chart -> Indicators List -> Select this indicator -> Properties",
                       "Indicator Settings Info", MB_ICONINFORMATION);
        }
    }
}
 
Ismail Lemin #:

Hello Miguel hope you are doing well. I did try to use the OBJPROP_PRICE1 and 2 but it gives errors . can i share the same indicator that has IIFVG arrow whcih still wont delete once invalidated. If they can delete its when i shift to drawing IFVG boxes instead of arrow. Kindly help please will appreciate it so much. The fvg part is very okay drawing hidding and deleting is smooth. Just the issue with IFVG arrows that wont delete. 

At first, we were talking about the version with rectangles, and the issue there is that you're using OBJPROP_PRICE, but for OBJ_RECTANGLE you need to use OBJPROP_PRICE1 and OBJPROP_PRICE2. Since that call fails, the zones never get deleted, even when they're clearly broken.

Then you slightly shifted the focus and mentioned arrows (OBJ_ARROW) with iFVGs, which is a different context.

In that case, deletion isn't really broken, you just need a proper function to loop through the chart objects and remove the invalidated arrows based on their name or status.

 
Miguel Angel Vico Alba #:

At first, we were talking about the version with rectangles, and the issue there is that you're using OBJPROP_PRICE, but for OBJ_RECTANGLE you need to use OBJPROP_PRICE1 and OBJPROP_PRICE2. Since that call fails, the zones never get deleted, even when they're clearly broken.

Then you slightly shifted the focus and mentioned arrows (OBJ_ARROW) with iFVGs, which is a different context.

In that case, deletion isn't really broken, you just need a proper function to loop through the chart objects and remove the invalidated arrows based on their name or status.


Can you help with please. Honestly it's been a week and I have been going in circles trying the rectangles and the rectangles which are just the same indicator with slight modifications. I have evn tried to use chatgpt to find a proper loop for the functions and looping but it hasn't worked. Kindly help me. Am totally stuck. 
 
Ismail Hassan Lemin #:
Can you help with please. Honestly it's been a week and I have been going in circles trying the rectangles and the rectangles which are just the same indicator with slight modifications. I have evn tried to use chatgpt to find a proper loop for the functions and looping but it hasn't worked. Kindly help me. Am totally stuck. 
Kindly mate especially the arrows which is simpler compared to the boxes. As youve mentioned, In that case, deletion isn't really broken, you just need a proper function to loop through the chart objects and remove the invalidated arrows based on their name or status.