City Times


London

Paris

New York

Tokyo

Sydney

FBS Top

Google Search

Saturday, May 10, 2025

Mql5 code compiled but not printing on chart

  #property copyright "Copyright 2025, MT5 Indicator"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// Enum for session types
enum ENUM_SESSION_TYPE {
   SESSION_LONDON,   // London
   SESSION_NEWYORK,  // New York
   SESSION_SYDNEY,   // Sydney
   SESSION_ASIA      // Asia
};

// Input parameters
input bool           ShowSessionLabels = true;  // Show Session Labels
input bool           ShowDayLabels = true;      // Show Day Labels
input bool           ShowWeekLabels = true;     // Show Week Labels
input bool           ShowMonthLabels = true;    // Show Month Labels
input bool           ShowYearLabels = true;     // Show Year Labels
input string         LabelFont = "Arial";       // Label Font
input int            LabelFontSize = 8;         // Label Font Size
input color          PositiveColor = clrGreen;  // Positive Label Color
input color          NegativeColor = clrRed;    // Negative Label Color
input color          OpenRangeColor = clrBlue;  // Open Range Label Color
input int            LabelHorizontalOffset = 5; // Label Horizontal Offset (pixels)
input int            LabelVerticalSpacing = 15; // Label Vertical Spacing (pixels)

// Anti-cluttering parameters
input int            MinBarsBetweenLabels = 3;  // Minimum bars between labels
input double         MinPriceDistanceFactor = 0.3; // Price distance factor (in ATR)

// Session time inputs
input string         SessionSeparator = "------- Session Times -------";
input int            LondonOpenHour = 8;        // London Open Hour (GMT)
input int            LondonCloseHour = 16;      // London Close Hour (GMT)
input int            NewYorkOpenHour = 13;      // New York Open Hour (GMT)
input int            NewYorkCloseHour = 21;     // New York Close Hour (GMT)
input int            SydneyOpenHour = 22;       // Sydney Open Hour (GMT)
input int            SydneyCloseHour = 6;       // Sydney Close Hour (GMT)
input int            AsiaOpenHour = 0;          // Asia Open Hour (GMT)
input int            AsiaCloseHour = 9;         // Asia Close Hour (GMT)

// Structure to store wave points
struct WavePoint {
   datetime time;
   double   price;
   string   label;
   bool     isPositive;
   int      type;        // 0=target, 1=flip, 2=high/low
   string   objectName;  // To store the name of the created object for reference
};

// Point tracking information for anti-cluttering logic
struct PointTracker {
   datetime lastLabelTime;
   double   lastLabelPrice;
   int      lastLabelDirection;  // 1 for above price, -1 for below price
   int      verticalCounter;     // Used to stack labels vertically when needed
};

// Arrays to store wave points for different timeframes
WavePoint sessionPoints[];
WavePoint dayPoints[];
WavePoint weekPoints[];
WavePoint monthPoints[];
WavePoint yearPoints[];

// Point trackers for different timeframes
PointTracker sessionTracker;
PointTracker dayTracker;
PointTracker weekTracker;
PointTracker monthTracker;
PointTracker yearTracker;

// Session tracking variables
struct TimeframeData {
   datetime openRangeTime;
   double   openRangeHigh;
   double   openRangeLow;
   bool     directionPositive;
   int      state;       // 0=initial, 1=target, 2=flip, 3=high/low
   datetime lastExtremumTime;
   double   lowestLowSinceOpenRange;
   double   highestHighSinceOpenRange;
   datetime lowestLowTime;
   datetime highestHighTime;
};

// Timeframe data structures
TimeframeData sessionData;
TimeframeData dayData;
TimeframeData weekData;
TimeframeData monthData;
TimeframeData yearData;

// Current session tracker
ENUM_SESSION_TYPE currentSession = SESSION_LONDON;
datetime lastSessionChangeTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
   // Set indicator properties
   IndicatorSetString(INDICATOR_SHORTNAME, "Open Range Wave Fractals");
   
   // Initialize timeframe data
   InitTimeframeData(sessionData);
   InitTimeframeData(dayData);
   InitTimeframeData(weekData);
   InitTimeframeData(monthData);
   InitTimeframeData(yearData);
   
   // Initialize point trackers
   InitPointTracker(sessionTracker);
   InitPointTracker(dayTracker);
   InitPointTracker(weekTracker);
   InitPointTracker(monthTracker);
   InitPointTracker(yearTracker);
   
   // Resize arrays
   ArrayResize(sessionPoints, 0);
   ArrayResize(dayPoints, 0);
   ArrayResize(weekPoints, 0);
   ArrayResize(monthPoints, 0);
   ArrayResize(yearPoints, 0);
   
   // Clean up any existing objects
   CleanupObjects();
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Initialize timeframe data structure                              |
//+------------------------------------------------------------------+
void InitTimeframeData(TimeframeData &data) {
   data.openRangeTime = 0;
   data.openRangeHigh = 0;
   data.openRangeLow = 0;
   data.directionPositive = false;
   data.state = 0;
   data.lastExtremumTime = 0;
   data.lowestLowSinceOpenRange = 0;
   data.highestHighSinceOpenRange = 0;
   data.lowestLowTime = 0;
   data.highestHighTime = 0;
}

//+------------------------------------------------------------------+
//| Initialize point tracker structure                               |
//+------------------------------------------------------------------+
void InitPointTracker(PointTracker &tracker) {
   tracker.lastLabelTime = 0;
   tracker.lastLabelPrice = 0;
   tracker.lastLabelDirection = 0;
   tracker.verticalCounter = 0;
}

// Forward declarations of functions
bool IsLabelCluttered(datetime time, double price, const PointTracker &tracker);
void CreateWavePointLabel(datetime time, double price, string text, bool isPositive,
                          int type, WavePoint &points[], string prefix, PointTracker &tracker);
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive);
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive);
void CreateLabel(string name, datetime time, double price, string text, color clr);
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time);
string GetSessionName(ENUM_SESSION_TYPE session);
void AddWavePoint(WavePoint &points[], WavePoint &point);
void CreateOpenRangeLabels(string prefix, datetime time, double high, double low,
                          string highLabel, string lowLabel, PointTracker &tracker);
bool IsPositionOccupied(datetime time, double price);
void FindOptimalLabelPosition(datetime &time, double &price, int &direction);
void UpdateExtremesAndProcessWave(const int index,
                                 const datetime &time[],
                                 const double &high[],
                                 const double &low[],
                                 TimeframeData &data,
                                 WavePoint &points[],
                                 const string suffix,
                                 PointTracker &tracker);

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[]) {
   // Calculate only new bars
   int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
   
   // Ensure we have at least 2 bars to analyze
   if(rates_total < 2) return(rates_total);
   
   // Loop through bars
   for(int i = start; i < rates_total; i++) {
      // Skip the first bar in the calculation
      if(i == 0) continue;
      
      // Get current and previous time structures
      MqlDateTime dt_prev, dt_curr;
      TimeToStruct(time[i-1], dt_prev);
      TimeToStruct(time[i], dt_curr);
      
      // Check for new day
      bool isNewDay = dt_prev.day != dt_curr.day;
      if(isNewDay && ShowDayLabels) {
         // New day detected
         dayData.openRangeTime = time[i-1];
         dayData.openRangeHigh = high[i-1];
         dayData.openRangeLow = low[i-1];
         dayData.directionPositive = false;
         dayData.state = 0;
         dayData.lowestLowSinceOpenRange = low[i];
         dayData.highestHighSinceOpenRange = high[i];
         dayData.lowestLowTime = time[i];
         dayData.highestHighTime = time[i];
         
         // Create day open range labels with anti-cluttering
         CreateOpenRangeLabels("DayOpenRange", time[i], dayData.openRangeHigh, dayData.openRangeLow, "ORH", "ORL", dayTracker);
      }
      
      // Check for new week
      bool isNewWeek = dt_prev.day_of_week > dt_curr.day_of_week;
      if(isNewWeek && ShowWeekLabels) {
         // New week detected
         weekData.openRangeTime = time[i-1];
         weekData.openRangeHigh = high[i-1];
         weekData.openRangeLow = low[i-1];
         weekData.directionPositive = false;
         weekData.state = 0;
         weekData.lowestLowSinceOpenRange = low[i];
         weekData.highestHighSinceOpenRange = high[i];
         weekData.lowestLowTime = time[i];
         weekData.highestHighTime = time[i];
         
         // Create week open range labels with anti-cluttering
         CreateOpenRangeLabels("WeekOpenRange", time[i], weekData.openRangeHigh, weekData.openRangeLow, "WK orH", "WK orL", weekTracker);
      }
      
      // Check for new month
      bool isNewMonth = dt_prev.mon != dt_curr.mon;
      if(isNewMonth && ShowMonthLabels) {
         // New month detected
         monthData.openRangeTime = time[i-1];
         monthData.openRangeHigh = high[i-1];
         monthData.openRangeLow = low[i-1];
         monthData.directionPositive = false;
         monthData.state = 0;
         monthData.lowestLowSinceOpenRange = low[i];
         monthData.highestHighSinceOpenRange = high[i];
         monthData.lowestLowTime = time[i];
         monthData.highestHighTime = time[i];
         
         // Create month open range labels with anti-cluttering
         CreateOpenRangeLabels("MonthOpenRange", time[i], monthData.openRangeHigh, monthData.openRangeLow, "M orH", "M orL", monthTracker);
      }
      
      // Check for new year
      bool isNewYear = dt_prev.year != dt_curr.year;
      if(isNewYear && ShowYearLabels) {
         // New year detected
         yearData.openRangeTime = time[i-1];
         yearData.openRangeHigh = high[i-1];
         yearData.openRangeLow = low[i-1];
         yearData.directionPositive = false;
         yearData.state = 0;
         yearData.lowestLowSinceOpenRange = low[i];
         yearData.highestHighSinceOpenRange = high[i];
         yearData.lowestLowTime = time[i];
         yearData.highestHighTime = time[i];
         
         // Create year open range labels with anti-cluttering
         CreateOpenRangeLabels("YearOpenRange", time[i], yearData.openRangeHigh, yearData.openRangeLow, "Y orH", "Y orL", yearTracker);
      }
      
      // Check for session change
      ENUM_SESSION_TYPE newSession = DetermineCurrentSession(time[i]);
      if(newSession != currentSession && ShowSessionLabels) {
         // New session detected
         sessionData.openRangeTime = time[i-1];
         sessionData.openRangeHigh = high[i-1];
         sessionData.openRangeLow = low[i-1];
         sessionData.directionPositive = false;
         sessionData.state = 0;
         sessionData.lowestLowSinceOpenRange = low[i];
         sessionData.highestHighSinceOpenRange = high[i];
         sessionData.lowestLowTime = time[i];
         sessionData.highestHighTime = time[i];
         
         // Get session name and create appropriate labels
         string sessionName = GetSessionName(newSession);
         string sessionHighLabel = "";
         string sessionLowLabel = "";
         
         if(sessionName == "London") {
            sessionHighLabel = "LN orH";
            sessionLowLabel = "LN orL";
         } else if(sessionName == "New York") {
            sessionHighLabel = "NY orH";
            sessionLowLabel = "NY orL";
         } else if(sessionName == "Sydney") {
            sessionHighLabel = "SY orH";
            sessionLowLabel = "SY orL";
         } else if(sessionName == "Asia") {
            sessionHighLabel = "AS orH";
            sessionLowLabel = "AS orL";
         }
         
         // Create session open range labels with anti-cluttering
         CreateOpenRangeLabels("SessionOpenRange", time[i], sessionData.openRangeHigh, sessionData.openRangeLow, sessionHighLabel, sessionLowLabel, sessionTracker);
         
         currentSession = newSession;
         lastSessionChangeTime = time[i];
      }
      
      // Update extremes and check for wave points for each timeframe
      if(i > 0) {
         // Process wave points for sessions
         if(ShowSessionLabels && sessionData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, sessionData, sessionPoints, "S", sessionTracker);
         }
         
         // Process wave points for days
         if(ShowDayLabels && dayData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, dayData, dayPoints, "D", dayTracker);
         }
         
         // Process wave points for weeks
         if(ShowWeekLabels && weekData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, weekData, weekPoints, "W", weekTracker);
         }
         
         // Process wave points for months
         if(ShowMonthLabels && monthData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, monthData, monthPoints, "M", monthTracker);
         }
         
         // Process wave points for years
         if(ShowYearLabels && yearData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, yearData, yearPoints, "Y", yearTracker);
         }
      }
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Create open range labels with anti-cluttering                    |
//+------------------------------------------------------------------+
void CreateOpenRangeLabels(string prefix, datetime time, double high, double low,
                          string highLabel, string lowLabel, PointTracker &tracker) {
   // Create unique names for high and low labels
   string labelNameHigh = prefix + "High_" + TimeToString(time);
   string labelNameLow = prefix + "Low_" + TimeToString(time);
   
   // Find optimal positions to avoid cluttering
   datetime highTime = time;
   datetime lowTime = time;
   double highPrice = high;
   double lowPrice = low;
   int highDirection = 0;
   int lowDirection = 0;
   
   if(IsLabelCluttered(highTime, highPrice, tracker)) {
      FindOptimalLabelPosition(highTime, highPrice, highDirection);
   }
   
   if(IsLabelCluttered(lowTime, lowPrice, tracker)) {
      FindOptimalLabelPosition(lowTime, lowPrice, lowDirection);
   }
   
   // Create text objects with found positions
   CreateLabel(labelNameHigh, highTime, highPrice, highLabel, OpenRangeColor);
   CreateLabel(labelNameLow, lowTime, lowPrice, lowLabel, OpenRangeColor);
   
   // Update tracker
   tracker.lastLabelTime = time;
   tracker.lastLabelPrice = (high + low) / 2; // Use middle price as reference
   tracker.lastLabelDirection = 0; // Reset direction
   tracker.verticalCounter = 0;    // Reset counter for next round
}

//+------------------------------------------------------------------+
//| Check if a label would be cluttered at the given position        |
//+------------------------------------------------------------------+
bool IsLabelCluttered(datetime time, double price, const PointTracker &tracker) {
   if(tracker.lastLabelTime == 0) return false; // First label
   
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14);
   
   // Check time proximity
   int barsBetween = (int)MathAbs((time - tracker.lastLabelTime) / PeriodSeconds());
   if(barsBetween < MinBarsBetweenLabels) {
      // Time is too close, check if price is also close
      double priceDiff = MathAbs(price - tracker.lastLabelPrice);
      
      // Dynamic price threshold based on ATR
      double priceThreshold = atr * MinPriceDistanceFactor;
      
      if(priceDiff < priceThreshold) {
         return true; // Too cluttered
      }
   }
   
   // Check for any existing objects in proximity
   int totalObjects = ObjectsTotal(0);
   for(int i = 0; i < totalObjects; i++) {
      string objName = ObjectName(0, i);
      
      // Skip objects that aren't text labels
      if(ObjectGetInteger(0, objName, OBJPROP_TYPE) != OBJ_TEXT) continue;
      
      // Get object properties
      datetime objTime = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME);
      double objPrice = ObjectGetDouble(0, objName, OBJPROP_PRICE);
      
      // Check if it's too close
      int objBarDiff = (int)MathAbs((time - objTime) / PeriodSeconds());
      double objPriceDiff = MathAbs(price - objPrice);
      
      if(objBarDiff < MinBarsBetweenLabels && objPriceDiff < (atr * MinPriceDistanceFactor)) {
         return true; // Cluttered with an existing object
      }
   }
   
   return false; // Not cluttered
}

//+------------------------------------------------------------------+
//| Find optimal position for label to avoid cluttering              |
//+------------------------------------------------------------------+
void FindOptimalLabelPosition(datetime &time, double &price, int &direction) {
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14);
   double verticalStep = atr * 0.5; // Half ATR for vertical stepping
   
   // Try up to 5 different vertical positions
   for(int i = 1; i <= 5; i++) {
      // Try above the price
      double testPrice = price + (verticalStep * i);
      if(!IsPositionOccupied(time, testPrice)) {
         price = testPrice;
         direction = 1;
         return;
      }
      
      // Try below the price
      testPrice = price - (verticalStep * i);
      if(!IsPositionOccupied(time, testPrice)) {
         price = testPrice;
         direction = -1;
         return;
      }
   }
   
   // If we couldn't find a good vertical position, try horizontal offset
   for(int i = 1; i <= 3; i++) {
      datetime testTime = time + (PeriodSeconds() * i);
      if(!IsPositionOccupied(testTime, price)) {
         time = testTime;
         direction = 0;
         return;
      }
   }
   
   // If all else fails, keep the original position
   direction = 0;
}

//+------------------------------------------------------------------+
//| Check if a position is already occupied by another label         |
//+------------------------------------------------------------------+
bool IsPositionOccupied(datetime time, double price) {
   int totalObjects = ObjectsTotal(0);
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14);
   double priceThreshold = atr * 0.3; // 30% of ATR
   
   for(int i = 0; i < totalObjects; i++) {
      string objName = ObjectName(0, i);
      
      // Skip objects that aren't text labels
      if(ObjectGetInteger(0, objName, OBJPROP_TYPE) != OBJ_TEXT) continue;
      
      // Get object properties
      datetime objTime = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME);
      double objPrice = ObjectGetDouble(0, objName, OBJPROP_PRICE);
      
      // Check if it's too close (3 bars and 30% ATR)
      int objBarDiff = (int)MathAbs((time - objTime) / PeriodSeconds());
      double objPriceDiff = MathAbs(price - objPrice);
      
      if(objBarDiff < 3 && objPriceDiff < priceThreshold) {
         return true; // Position is occupied
      }
   }
   
   return false; // Position is free
}

//+------------------------------------------------------------------+
//| Update extremes and process wave points with anti-cluttering     |
//+------------------------------------------------------------------+
void UpdateExtremesAndProcessWave(const int index,
                                 const datetime &time[],
                                 const double &high[],
                                 const double &low[],
                                 TimeframeData &data,
                                 WavePoint &points[],
                                 const string suffix,
                                 PointTracker &tracker) {
   // Update tracking extremes
   if(low[index] < data.lowestLowSinceOpenRange) {
      data.lowestLowSinceOpenRange = low[index];
      data.lowestLowTime = time[index];
   }
   
   if(high[index] > data.highestHighSinceOpenRange) {
      data.highestHighSinceOpenRange = high[index];
      data.highestHighTime = time[index];
   }
   
   // Initial direction determination
   if(data.state == 0) {
      if(low[index] < data.openRangeLow && high[index] <= data.openRangeHigh) {
         // Price moved lower first
         data.directionPositive = false;
         data.state = 1; // Moving to target state
      }
      else if(high[index] > data.openRangeHigh && low[index] >= data.openRangeLow) {
         // Price moved higher first
         data.directionPositive = true;
         data.state = 1; // Moving to target state
      }
   }
   
   // Process based on current state and direction
   switch(data.state) {
      case 1: // Target state
         if(!data.directionPositive) {
            // Negative direction, looking for a high above open range high
            if(high[index] > data.openRangeHigh) {
               // Create target label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange,
                                  "-" + suffix, false, 0, points, suffix + "_Target", tracker);
               
               data.state = 2; // Move to flip state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // Positive direction, looking for a low below open range low
            if(low[index] < data.openRangeLow) {
               // Create target label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange,
                                  "+" + suffix, true, 0, points, suffix + "_Target", tracker);
               
               data.state = 2; // Move to flip state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
         
      case 2: // Flip state
         if(!data.directionPositive) {
            // We're in negative direction, check if price breaks below previous target
            double lastTarget = GetLastTargetPriceWithSign(points, false);
            if(lastTarget > 0 && low[index] < lastTarget) {
               // Create flip label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange,
                                  "-" + suffix, false, 1, points, suffix + "_Flip", tracker);
               
               data.state = 3; // Move to high/low state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // We're in positive direction, check if price breaks above previous target
            double lastTarget = GetLastTargetPriceWithSign(points, true);
            if(lastTarget > 0 && high[index] > lastTarget) {
               // Create flip label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange,
                                  "+" + suffix, true, 1, points, suffix + "_Flip", tracker);
               
               data.state = 3; // Move to high/low state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
         
      case 3: // High/Low state
         if(!data.directionPositive) {
            // We're in negative direction, check if price goes above flip
            double lastFlip = GetLastFlipPriceWithSign(points, false);
            if(lastFlip > 0 && high[index] > lastFlip) {
               // Create low label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange,
                                  "-" + suffix, false, 2, points, suffix + "_Low", tracker);
               
               // Switch direction for new cycle
               data.directionPositive = true;
               data.state = 1; // Back to target state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // We're in positive direction, check if price goes below flip
            double lastFlip = GetLastFlipPriceWithSign(points, true);
            if(lastFlip > 0 && low[index] < lastFlip) {
               // Create high label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange,
                                  "+" + suffix, true, 2, points, suffix + "_High", tracker);
               
               // Switch direction for new cycle
               data.directionPositive = false;
               data.state = 1; // Back to target state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
   }
}

//+------------------------------------------------------------------+
//| Create wave point label with anti-cluttering                     |
//+------------------------------------------------------------------+
void CreateWavePointLabel(datetime time, double price, string text, bool isPositive,
                          int type, WavePoint &points[], string prefix, PointTracker &tracker) {
   // Create base object name
   string objName = prefix + "_" + TimeToString(time);
   
   // Find optimal position to avoid cluttering
   datetime labelTime = time;
   double labelPrice = price;
   int direction = 0;
   
   if(IsLabelCluttered(labelTime, labelPrice, tracker)) {
      FindOptimalLabelPosition(labelTime, labelPrice, direction);
   }
   
   // Create wave point structure
   WavePoint point;
   point.time = time;
   point.price = price;
   point.label = text;
   point.isPositive = isPositive;
   point.type = type;
   point.objectName = objName;
   AddWavePoint(points, point);
   
   // Create the actual text label on chart
color labelColor = isPositive ? PositiveColor : NegativeColor;
CreateLabel(objName, labelTime, labelPrice, text, labelColor);

// Update tracker
tracker.lastLabelTime = time;
tracker.lastLabelPrice = price;
tracker.lastLabelDirection = direction;
tracker.verticalCounter++;
}

//+------------------------------------------------------------------+
//| Add wave point to array                                          |
//+------------------------------------------------------------------+
void AddWavePoint(WavePoint &points[], WavePoint &point) {
   int size = ArraySize(points);
   ArrayResize(points, size + 1);
   points[size] = point;
}

//+------------------------------------------------------------------+
//| Create text label on chart                                       |
//+------------------------------------------------------------------+
void CreateLabel(string name, datetime time, double price, string text, color clr) {
   // Create text object
   ObjectCreate(0, name, OBJ_TEXT, 0, time, price);
   
   // Set label properties
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetString(0, name, OBJPROP_FONT, LabelFont);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, LabelFontSize);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   
   // Apply horizontal offset
   int x_offset = LabelHorizontalOffset;
   ObjectSetInteger(0, name, OBJPROP_XOFFSET, x_offset);
}

//+------------------------------------------------------------------+
//| Get last target price with correct sign                          |
//+------------------------------------------------------------------+
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive) {
   // Start from the end and find the most recent target point with matching sign
   for(int i = ArraySize(points) - 1; i >= 0; i--) {
      if(points[i].type == 0 && points[i].isPositive == positive) {
         return points[i].price;
      }
   }
   
   return 0; // Not found
}

//+------------------------------------------------------------------+
//| Get last flip price with correct sign                            |
//+------------------------------------------------------------------+
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive) {
   // Start from the end and find the most recent flip point with matching sign
   for(int i = ArraySize(points) - 1; i >= 0; i--) {
      if(points[i].type == 1 && points[i].isPositive == positive) {
         return points[i].price;
      }
   }
   
   return 0; // Not found
}

//+------------------------------------------------------------------+
//| Determine current trading session based on time                  |
//+------------------------------------------------------------------+
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time) {
   MqlDateTime dt;
   TimeToStruct(time, dt);
   
   // Adjust to GMT hours
   int currentHour = dt.hour;
   
   // Check which session we're in
   if(currentHour >= LondonOpenHour && currentHour < LondonCloseHour) {
      return SESSION_LONDON;
   } else if(currentHour >= NewYorkOpenHour && currentHour < NewYorkCloseHour) {
      return SESSION_NEWYORK;
   } else if((currentHour >= SydneyOpenHour) ||
            (currentHour < SydneyCloseHour)) {
      // Handle overnight session (Sydney crosses midnight)
      return SESSION_SYDNEY;
   } else if(currentHour >= AsiaOpenHour && currentHour < AsiaCloseHour) {
      return SESSION_ASIA;
   }
   
   // Default to London if none match (should not happen with proper settings)
   return SESSION_LONDON;
}

//+------------------------------------------------------------------+
//| Get session name as string                                       |
//+------------------------------------------------------------------+
string GetSessionName(ENUM_SESSION_TYPE session) {
   switch(session) {
      case SESSION_LONDON:  return "London";
      case SESSION_NEWYORK: return "New York";
      case SESSION_SYDNEY:  return "Sydney";
      case SESSION_ASIA:    return "Asia";
      default:              return "Unknown";
   }
}

//+------------------------------------------------------------------+
//| Clean up objects when indicator is removed                       |
//+------------------------------------------------------------------+
void CleanupObjects() {
   // Clean up open range labels
   string prefixes[] = {"DayOpenRange", "WeekOpenRange", "MonthOpenRange",
                        "YearOpenRange", "SessionOpenRange"};
   
   for(int i = 0; i < ArraySize(prefixes); i++) {
      ObjectsDeleteAll(0, prefixes[i]);
   }
   
   // Clean up wave point labels
   string suffixes[] = {"_Target", "_Flip", "_High", "_Low"};
   string timeframes[] = {"D", "W", "M", "Y", "S"};
   
   for(int i = 0; i < ArraySize(timeframes); i++) {
      for(int j = 0; j < ArraySize(suffixes); j++) {
         string prefix = timeframes[i] + suffixes[j];
         ObjectsDeleteAll(0, prefix);
      }
   }
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // Clean up all objects created by this indicator
   CleanupObjects();
}


Breakaware, copyright2012©

Mql5 Error generated

 


_____________________________________________________________________

#property copyright "Copyright 2025, MT5 Indicator"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// Enum for session types
enum ENUM_SESSION_TYPE {
   SESSION_LONDON,   // London
   SESSION_NEWYORK,  // New York
   SESSION_SYDNEY,   // Sydney
   SESSION_ASIA      // Asia
};

// Input parameters
input bool           ShowSessionLabels = true;  // Show Session Labels
input bool           ShowDayLabels = true;      // Show Day Labels
input bool           ShowWeekLabels = true;     // Show Week Labels
input bool           ShowMonthLabels = true;    // Show Month Labels
input bool           ShowYearLabels = true;     // Show Year Labels
input string         LabelFont = "Arial";       // Label Font
input int            LabelFontSize = 8;         // Label Font Size
input color          PositiveColor = clrGreen;  // Positive Label Color
input color          NegativeColor = clrRed;    // Negative Label Color
input color          OpenRangeColor = clrBlue;  // Open Range Label Color
input int            LabelHorizontalOffset = 5; // Label Horizontal Offset (pixels)
input int            LabelVerticalSpacing = 15; // Label Vertical Spacing (pixels)

// Anti-cluttering parameters
input int            MinBarsBetweenLabels = 3;  // Minimum bars between labels
input double         MinPriceDistanceFactor = 0.3; // Price distance factor (in ATR)

// Session time inputs
input string         SessionSeparator = "------- Session Times -------";
input int            LondonOpenHour = 8;        // London Open Hour (GMT)
input int            LondonCloseHour = 16;      // London Close Hour (GMT)
input int            NewYorkOpenHour = 13;      // New York Open Hour (GMT)
input int            NewYorkCloseHour = 21;     // New York Close Hour (GMT)
input int            SydneyOpenHour = 22;       // Sydney Open Hour (GMT)
input int            SydneyCloseHour = 6;       // Sydney Close Hour (GMT)
input int            AsiaOpenHour = 0;          // Asia Open Hour (GMT)
input int            AsiaCloseHour = 9;         // Asia Close Hour (GMT)

// Structure to store wave points
struct WavePoint {
   datetime time;
   double   price;
   string   label;
   bool     isPositive;
   int      type;        // 0=target, 1=flip, 2=high/low
   string   objectName;  // To store the name of the created object for reference
};

// Point tracking information for anti-cluttering logic
struct PointTracker {
   datetime lastLabelTime;
   double   lastLabelPrice;
   int      lastLabelDirection;  // 1 for above price, -1 for below price
   int      verticalCounter;     // Used to stack labels vertically when needed
};

// Arrays to store wave points for different timeframes
WavePoint sessionPoints[];
WavePoint dayPoints[];
WavePoint weekPoints[];
WavePoint monthPoints[];
WavePoint yearPoints[];

// Point trackers for different timeframes
PointTracker sessionTracker;
PointTracker dayTracker;
PointTracker weekTracker;
PointTracker monthTracker;
PointTracker yearTracker;

// Session tracking variables
struct TimeframeData {
   datetime openRangeTime;
   double   openRangeHigh;
   double   openRangeLow;
   bool     directionPositive;
   int      state;       // 0=initial, 1=target, 2=flip, 3=high/low
   datetime lastExtremumTime;
   double   lowestLowSinceOpenRange;
   double   highestHighSinceOpenRange;
   datetime lowestLowTime;
   datetime highestHighTime;
};

// Timeframe data structures
TimeframeData sessionData;
TimeframeData dayData;
TimeframeData weekData;
TimeframeData monthData;
TimeframeData yearData;

// Current session tracker
ENUM_SESSION_TYPE currentSession = SESSION_LONDON;
datetime lastSessionChangeTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
   // Set indicator properties
   IndicatorSetString(INDICATOR_SHORTNAME, "Open Range Wave Fractals");
   
   // Initialize timeframe data
   InitTimeframeData(sessionData);
   InitTimeframeData(dayData);
   InitTimeframeData(weekData);
   InitTimeframeData(monthData);
   InitTimeframeData(yearData);
   
   // Initialize point trackers
   InitPointTracker(sessionTracker);
   InitPointTracker(dayTracker);
   InitPointTracker(weekTracker);
   InitPointTracker(monthTracker);
   InitPointTracker(yearTracker);
   
   // Resize arrays
   ArrayResize(sessionPoints, 0);
   ArrayResize(dayPoints, 0);
   ArrayResize(weekPoints, 0);
   ArrayResize(monthPoints, 0);
   ArrayResize(yearPoints, 0);
   
   // Clean up any existing objects
   CleanupObjects();
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Initialize timeframe data structure                              |
//+------------------------------------------------------------------+
void InitTimeframeData(TimeframeData &data) {
   data.openRangeTime = 0;
   data.openRangeHigh = 0;
   data.openRangeLow = 0;
   data.directionPositive = false;
   data.state = 0;
   data.lastExtremumTime = 0;
   data.lowestLowSinceOpenRange = 0;
   data.highestHighSinceOpenRange = 0;
   data.lowestLowTime = 0;
   data.highestHighTime = 0;
}

//+------------------------------------------------------------------+
//| Initialize point tracker structure                               |
//+------------------------------------------------------------------+
void InitPointTracker(PointTracker &tracker) {
   tracker.lastLabelTime = 0;
   tracker.lastLabelPrice = 0;
   tracker.lastLabelDirection = 0;
   tracker.verticalCounter = 0;
}

// Forward declarations of functions
bool IsLabelCluttered(datetime time, double price, const PointTracker &tracker);
void CreateWavePointLabel(datetime time, double price, string text, bool isPositive, 
                          int type, WavePoint &points[], string prefix, PointTracker &tracker);
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive);
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive);
void CreateLabel(string name, datetime time, double price, string text, color clr);
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time);
string GetSessionName(ENUM_SESSION_TYPE session);
void AddWavePoint(WavePoint &points[], WavePoint &point);
void CreateOpenRangeLabels(string prefix, datetime time, double high, double low, 
                          string highLabel, string lowLabel, PointTracker &tracker);
bool IsPositionOccupied(datetime time, double price);
void FindOptimalLabelPosition(datetime &time, double &price, int &direction);
void UpdateExtremesAndProcessWave(const int index,
                                 const datetime &time[],
                                 const double &high[],
                                 const double &low[],
                                 TimeframeData &data,
                                 WavePoint &points[],
                                 const string suffix,
                                 PointTracker &tracker);

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[]) {
   // Calculate only new bars
   int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
   
   // Ensure we have at least 2 bars to analyze
   if(rates_total < 2) return(rates_total);
   
   // Loop through bars
   for(int i = start; i < rates_total; i++) {
      // Skip the first bar in the calculation
      if(i == 0) continue;
      
      // Get current and previous time structures
      MqlDateTime dt_prev, dt_curr;
      TimeToStruct(time[i-1], dt_prev);
      TimeToStruct(time[i], dt_curr);
      
      // Check for new day
      bool isNewDay = dt_prev.day != dt_curr.day;
      if(isNewDay && ShowDayLabels) {
         // New day detected
         dayData.openRangeTime = time[i-1];
         dayData.openRangeHigh = high[i-1];
         dayData.openRangeLow = low[i-1];
         dayData.directionPositive = false;
         dayData.state = 0;
         dayData.lowestLowSinceOpenRange = low[i];
         dayData.highestHighSinceOpenRange = high[i];
         dayData.lowestLowTime = time[i];
         dayData.highestHighTime = time[i];
         
         // Create day open range labels with anti-cluttering
         CreateOpenRangeLabels("DayOpenRange", time[i], dayData.openRangeHigh, dayData.openRangeLow, "ORH", "ORL", dayTracker);
      }
      
      // Check for new week
      bool isNewWeek = dt_prev.day_of_week > dt_curr.day_of_week;
      if(isNewWeek && ShowWeekLabels) {
         // New week detected
         weekData.openRangeTime = time[i-1];
         weekData.openRangeHigh = high[i-1];
         weekData.openRangeLow = low[i-1];
         weekData.directionPositive = false;
         weekData.state = 0;
         weekData.lowestLowSinceOpenRange = low[i];
         weekData.highestHighSinceOpenRange = high[i];
         weekData.lowestLowTime = time[i];
         weekData.highestHighTime = time[i];
         
         // Create week open range labels with anti-cluttering
         CreateOpenRangeLabels("WeekOpenRange", time[i], weekData.openRangeHigh, weekData.openRangeLow, "WK orH", "WK orL", weekTracker);
      }
      
      // Check for new month
      bool isNewMonth = dt_prev.mon != dt_curr.mon;
      if(isNewMonth && ShowMonthLabels) {
         // New month detected
         monthData.openRangeTime = time[i-1];
         monthData.openRangeHigh = high[i-1];
         monthData.openRangeLow = low[i-1];
         monthData.directionPositive = false;
         monthData.state = 0;
         monthData.lowestLowSinceOpenRange = low[i];
         monthData.highestHighSinceOpenRange = high[i];
         monthData.lowestLowTime = time[i];
         monthData.highestHighTime = time[i];
         
         // Create month open range labels with anti-cluttering
         CreateOpenRangeLabels("MonthOpenRange", time[i], monthData.openRangeHigh, monthData.openRangeLow, "M orH", "M orL", monthTracker);
      }
      
      // Check for new year
      bool isNewYear = dt_prev.year != dt_curr.year;
      if(isNewYear && ShowYearLabels) {
         // New year detected
         yearData.openRangeTime = time[i-1];
         yearData.openRangeHigh = high[i-1];
         yearData.openRangeLow = low[i-1];
         yearData.directionPositive = false;
         yearData.state = 0;
         yearData.lowestLowSinceOpenRange = low[i];
         yearData.highestHighSinceOpenRange = high[i];
         yearData.lowestLowTime = time[i];
         yearData.highestHighTime = time[i];
         
         // Create year open range labels with anti-cluttering
         CreateOpenRangeLabels("YearOpenRange", time[i], yearData.openRangeHigh, yearData.openRangeLow, "Y orH", "Y orL", yearTracker);
      }
      
      // Check for session change
      ENUM_SESSION_TYPE newSession = DetermineCurrentSession(time[i]);
      if(newSession != currentSession && ShowSessionLabels) {
         // New session detected
         sessionData.openRangeTime = time[i-1];
         sessionData.openRangeHigh = high[i-1];
         sessionData.openRangeLow = low[i-1];
         sessionData.directionPositive = false;
         sessionData.state = 0;
         sessionData.lowestLowSinceOpenRange = low[i];
         sessionData.highestHighSinceOpenRange = high[i];
         sessionData.lowestLowTime = time[i];
         sessionData.highestHighTime = time[i];
         
         // Get session name and create appropriate labels
         string sessionName = GetSessionName(newSession);
         string sessionHighLabel = "";
         string sessionLowLabel = "";
         
         if(sessionName == "London") {
            sessionHighLabel = "LN orH";
            sessionLowLabel = "LN orL";
         } else if(sessionName == "New York") {
            sessionHighLabel = "NY orH";
            sessionLowLabel = "NY orL";
         } else if(sessionName == "Sydney") {
            sessionHighLabel = "SY orH";
            sessionLowLabel = "SY orL";
         } else if(sessionName == "Asia") {
            sessionHighLabel = "AS orH";
            sessionLowLabel = "AS orL";
         }
         
         // Create session open range labels with anti-cluttering
         CreateOpenRangeLabels("SessionOpenRange", time[i], sessionData.openRangeHigh, sessionData.openRangeLow, sessionHighLabel, sessionLowLabel, sessionTracker);
         
         currentSession = newSession;
         lastSessionChangeTime = time[i];
      }
      
      // Update extremes and check for wave points for each timeframe
      if(i > 0) {
         // Process wave points for sessions
         if(ShowSessionLabels && sessionData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, sessionData, sessionPoints, "S", sessionTracker);
         }
         
         // Process wave points for days
         if(ShowDayLabels && dayData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, dayData, dayPoints, "D", dayTracker);
         }
         
         // Process wave points for weeks
         if(ShowWeekLabels && weekData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, weekData, weekPoints, "W", weekTracker);
         }
         
         // Process wave points for months
         if(ShowMonthLabels && monthData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, monthData, monthPoints, "M", monthTracker);
         }
         
         // Process wave points for years
         if(ShowYearLabels && yearData.openRangeTime > 0) {
            UpdateExtremesAndProcessWave(i, time, high, low, yearData, yearPoints, "Y", yearTracker);
         }
      }
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Create open range labels with anti-cluttering                    |
//+------------------------------------------------------------------+
void CreateOpenRangeLabels(string prefix, datetime time, double high, double low, 
                          string highLabel, string lowLabel, PointTracker &tracker) {
   // Create unique names for high and low labels
   string labelNameHigh = prefix + "High_" + TimeToString(time);
   string labelNameLow = prefix + "Low_" + TimeToString(time);
   
   // Find optimal positions to avoid cluttering
   datetime highTime = time;
   datetime lowTime = time;
   double highPrice = high;
   double lowPrice = low;
   int highDirection = 0;
   int lowDirection = 0;
   
   if(IsLabelCluttered(highTime, highPrice, tracker)) {
      FindOptimalLabelPosition(highTime, highPrice, highDirection);
   }
   
   if(IsLabelCluttered(lowTime, lowPrice, tracker)) {
      FindOptimalLabelPosition(lowTime, lowPrice, lowDirection);
   }
   
   // Create text objects with found positions
   CreateLabel(labelNameHigh, highTime, highPrice, highLabel, OpenRangeColor);
   CreateLabel(labelNameLow, lowTime, lowPrice, lowLabel, OpenRangeColor);
   
   // Update tracker
   tracker.lastLabelTime = time;
   tracker.lastLabelPrice = (high + low) / 2; // Use middle price as reference
   tracker.lastLabelDirection = 0; // Reset direction
   tracker.verticalCounter = 0;    // Reset counter for next round
}

//+------------------------------------------------------------------+
//| Check if a label would be cluttered at the given position        |
//+------------------------------------------------------------------+
bool IsLabelCluttered(datetime time, double price, const PointTracker &tracker) {
   if(tracker.lastLabelTime == 0) return false; // First label
   
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 0);
   
   // Check time proximity
   int barsBetween = (int)MathAbs((time - tracker.lastLabelTime) / PeriodSeconds());
   if(barsBetween < MinBarsBetweenLabels) {
      // Time is too close, check if price is also close
      double priceDiff = MathAbs(price - tracker.lastLabelPrice);
      
      // Dynamic price threshold based on ATR
      double priceThreshold = atr * MinPriceDistanceFactor;
      
      if(priceDiff < priceThreshold) {
         return true; // Too cluttered
      }
   }
   
   // Check for any existing objects in proximity
   int totalObjects = ObjectsTotal(0);
   for(int i = 0; i < totalObjects; i++) {
      string objName = ObjectName(0, i);
      
      // Skip objects that aren't text labels
      if(ObjectGetInteger(0, objName, OBJPROP_TYPE) != OBJ_TEXT) continue;
      
      // Get object properties
      datetime objTime = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME);
      double objPrice = ObjectGetDouble(0, objName, OBJPROP_PRICE);
      
      // Check if it's too close
      int objBarDiff = (int)MathAbs((time - objTime) / PeriodSeconds());
      double objPriceDiff = MathAbs(price - objPrice);
      
      if(objBarDiff < MinBarsBetweenLabels && objPriceDiff < (atr * MinPriceDistanceFactor)) {
         return true; // Cluttered with an existing object
      }
   }
   
   return false; // Not cluttered
}

//+------------------------------------------------------------------+
//| Find optimal position for label to avoid cluttering              |
//+------------------------------------------------------------------+
void FindOptimalLabelPosition(datetime &time, double &price, int &direction) {
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 0);
   double verticalStep = atr * 0.5; // Half ATR for vertical stepping
   
   // Try up to 5 different vertical positions
   for(int i = 1; i <= 5; i++) {
      // Try above the price
      double testPrice = price + (verticalStep * i);
      if(!IsPositionOccupied(time, testPrice)) {
         price = testPrice;
         direction = 1;
         return;
      }
      
      // Try below the price
      testPrice = price - (verticalStep * i);
      if(!IsPositionOccupied(time, testPrice)) {
         price = testPrice;
         direction = -1;
         return;
      }
   }
   
   // If we couldn't find a good vertical position, try horizontal offset
   for(int i = 1; i <= 3; i++) {
      datetime testTime = time + (PeriodSeconds() * i);
      if(!IsPositionOccupied(testTime, price)) {
         time = testTime;
         direction = 0;
         return;
      }
   }
   
   // If all else fails, keep the original position
   direction = 0;
}

//+------------------------------------------------------------------+
//| Check if a position is already occupied by another label         |
//+------------------------------------------------------------------+
bool IsPositionOccupied(datetime time, double price) {
   int totalObjects = ObjectsTotal(0);
   // Correct iATR function call
   double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 0);
   double priceThreshold = atr * 0.3; // 30% of ATR
   
   for(int i = 0; i < totalObjects; i++) {
      string objName = ObjectName(0, i);
      
      // Skip objects that aren't text labels
      if(ObjectGetInteger(0, objName, OBJPROP_TYPE) != OBJ_TEXT) continue;
      
      // Get object properties
      datetime objTime = (datetime)ObjectGetInteger(0, objName, OBJPROP_TIME);
      double objPrice = ObjectGetDouble(0, objName, OBJPROP_PRICE);
      
      // Check if it's too close (3 bars and 30% ATR)
      int objBarDiff = (int)MathAbs((time - objTime) / PeriodSeconds());
      double objPriceDiff = MathAbs(price - objPrice);
      
      if(objBarDiff < 3 && objPriceDiff < priceThreshold) {
         return true; // Position is occupied
      }
   }
   
   return false; // Position is free
}

//+------------------------------------------------------------------+
//| Update extremes and process wave points with anti-cluttering     |
//+------------------------------------------------------------------+
void UpdateExtremesAndProcessWave(const int index,
                                 const datetime &time[],
                                 const double &high[],
                                 const double &low[],
                                 TimeframeData &data,
                                 WavePoint &points[],
                                 const string suffix,
                                 PointTracker &tracker) {
   // Update tracking extremes
   if(low[index] < data.lowestLowSinceOpenRange) {
      data.lowestLowSinceOpenRange = low[index];
      data.lowestLowTime = time[index];
   }
   
   if(high[index] > data.highestHighSinceOpenRange) {
      data.highestHighSinceOpenRange = high[index];
      data.highestHighTime = time[index];
   }
   
   // Initial direction determination
   if(data.state == 0) {
      if(low[index] < data.openRangeLow && high[index] <= data.openRangeHigh) {
         // Price moved lower first
         data.directionPositive = false;
         data.state = 1; // Moving to target state
      }
      else if(high[index] > data.openRangeHigh && low[index] >= data.openRangeLow) {
         // Price moved higher first
         data.directionPositive = true;
         data.state = 1; // Moving to target state
      }
   }
   
   // Process based on current state and direction
   switch(data.state) {
      case 1: // Target state
         if(!data.directionPositive) {
            // Negative direction, looking for a high above open range high
            if(high[index] > data.openRangeHigh) {
               // Create target label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange, 
                                  "-" + suffix, false, 0, points, suffix + "_Target", tracker);
               
               data.state = 2; // Move to flip state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // Positive direction, looking for a low below open range low
            if(low[index] < data.openRangeLow) {
               // Create target label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange, 
                                  "+" + suffix, true, 0, points, suffix + "_Target", tracker);
               
               data.state = 2; // Move to flip state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
         
      case 2: // Flip state
         if(!data.directionPositive) {
            // We're in negative direction, check if price breaks below previous target
            double lastTarget = GetLastTargetPriceWithSign(points, false);
            if(lastTarget > 0 && low[index] < lastTarget) {
               // Create flip label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange, 
                                  "-" + suffix, false, 1, points, suffix + "_Flip", tracker);
               
               data.state = 3; // Move to high/low state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // We're in positive direction, check if price breaks above previous target
            double lastTarget = GetLastTargetPriceWithSign(points, true);
            if(lastTarget > 0 && high[index] > lastTarget) {
               // Create flip label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange, 
                                  "+" + suffix, true, 1, points, suffix + "_Flip", tracker);
               
               data.state = 3; // Move to high/low state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
         
      case 3: // High/Low state
         if(!data.directionPositive) {
            // We're in negative direction, check if price goes above flip
            double lastFlip = GetLastFlipPriceWithSign(points, false);
            if(lastFlip > 0 && high[index] > lastFlip) {
               // Create low label at the lowest low with anti-cluttering
               CreateWavePointLabel(data.lowestLowTime, data.lowestLowSinceOpenRange, 
                                  "-" + suffix, false, 2, points, suffix + "_Low", tracker);
               
               // Switch direction for new cycle
               data.directionPositive = true;
               data.state = 1; // Back to target state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         } else {
            // We're in positive direction, check if price goes below flip
            double lastFlip = GetLastFlipPriceWithSign(points, true);
            if(lastFlip > 0 && low[index] < lastFlip) {
               // Create high label at the highest high with anti-cluttering
               CreateWavePointLabel(data.highestHighTime, data.highestHighSinceOpenRange, 
                                  "+" + suffix, true, 2, points, suffix + "_High", tracker);
               
               // Switch direction for new cycle
               data.directionPositive = false;
               data.state = 1; // Back to target state
               
               // Reset tracking for next phase
               data.lowestLowSinceOpenRange = low[index];
               data.highestHighSinceOpenRange = high[index];
               data.lowestLowTime = time[index];
               data.highestHighTime = time[index];
            }
         }
         break;
   }
}

//+------------------------------------------------------------------+
//| Create wave point label with anti-cluttering                     |
//+------------------------------------------------------------------+
void CreateWavePointLabel(datetime time, double price, string text, bool isPositive, 
                          int type, WavePoint &points[], string prefix, PointTracker &tracker) {
   // Create base object name
   string objName = prefix + "_" + TimeToString(time);
   
   // Find optimal position to avoid cluttering
   datetime labelTime = time;
   double labelPrice = price;
   int direction = 0;
   
   if(IsLabelCluttered(labelTime, labelPrice, tracker)) {
      FindOptimalLabelPosition(labelTime, labelPrice, direction);
   }
   
   // Create wave point structure
   WavePoint point;
   point.time = time;
   point.price = price;
   point.label = text;
   point.isPositive = isPositive;
   point.type = type;
   point.objectName = objName;
   AddWavePoint(points, point);
   
   // Create the actual text label on chart
color labelColor = isPositive ? PositiveColor : NegativeColor;
CreateLabel(objName, labelTime, labelPrice, text, labelColor);

// Update tracker
tracker.lastLabelTime = time;
tracker.lastLabelPrice = price;
tracker.lastLabelDirection = direction;
tracker.verticalCounter++;
}

//+------------------------------------------------------------------+
//| Add wave point to array                                          |
//+------------------------------------------------------------------+
void AddWavePoint(WavePoint &points[], WavePoint &point) {
   int size = ArraySize(points);
   ArrayResize(points, size + 1);
   points[size] = point;
}

//+------------------------------------------------------------------+
//| Create text label on chart                                       |
//+------------------------------------------------------------------+
void CreateLabel(string name, datetime time, double price, string text, color clr) {
   // Create text object
   ObjectCreate(0, name, OBJ_TEXT, 0, time, price);
   
   // Set label properties
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetString(0, name, OBJPROP_FONT, LabelFont);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, LabelFontSize);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   
   // Apply horizontal offset
   int x_offset = LabelHorizontalOffset;
   ObjectSetInteger(0, name, OBJPROP_XOFFSET, x_offset);
}

//+------------------------------------------------------------------+
//| Get last target price with correct sign                          |
//+------------------------------------------------------------------+
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive) {
   // Start from the end and find the most recent target point with matching sign
   for(int i = ArraySize(points) - 1; i >= 0; i--) {
      if(points[i].type == 0 && points[i].isPositive == positive) {
         return points[i].price;
      }
   }
   
   return 0; // Not found
}

//+------------------------------------------------------------------+
//| Get last flip price with correct sign                            |
//+------------------------------------------------------------------+
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive) {
   // Start from the end and find the most recent flip point with matching sign
   for(int i = ArraySize(points) - 1; i >= 0; i--) {
      if(points[i].type == 1 && points[i].isPositive == positive) {
         return points[i].price;
      }
   }
   
   return 0; // Not found
}

//+------------------------------------------------------------------+
//| Determine current trading session based on time                  |
//+------------------------------------------------------------------+
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time) {
   MqlDateTime dt;
   TimeToStruct(time, dt);
   
   // Adjust to GMT hours
   int currentHour = dt.hour;
   
   // Check which session we're in
   if(currentHour >= LondonOpenHour && currentHour < LondonCloseHour) {
      return SESSION_LONDON;
   } else if(currentHour >= NewYorkOpenHour && currentHour < NewYorkCloseHour) {
      return SESSION_NEWYORK;
   } else if((currentHour >= SydneyOpenHour) || 
            (currentHour < SydneyCloseHour)) {
      // Handle overnight session (Sydney crosses midnight)
      return SESSION_SYDNEY;
   } else if(currentHour >= AsiaOpenHour && currentHour < AsiaCloseHour) {
      return SESSION_ASIA;
   }
   
   // Default to London if none match (should not happen with proper settings)
   return SESSION_LONDON;
}

//+------------------------------------------------------------------+
//| Get session name as string                                       |
//+------------------------------------------------------------------+
string GetSessionName(ENUM_SESSION_TYPE session) {
   switch(session) {
      case SESSION_LONDON:  return "London";
      case SESSION_NEWYORK: return "New York";
      case SESSION_SYDNEY:  return "Sydney";
      case SESSION_ASIA:    return "Asia";
      default:              return "Unknown";
   }
}

//+------------------------------------------------------------------+
//| Clean up objects when indicator is removed                       |
//+------------------------------------------------------------------+
void CleanupObjects() {
   // Clean up open range labels
   string prefixes[] = {"DayOpenRange", "WeekOpenRange", "MonthOpenRange", 
                        "YearOpenRange", "SessionOpenRange"};
   
   for(int i = 0; i < ArraySize(prefixes); i++) {
      ObjectsDeleteAll(0, prefixes[i]);
   }
   
   // Clean up wave point labels
   string suffixes[] = {"_Target", "_Flip", "_High", "_Low"};
   string timeframes[] = {"D", "W", "M", "Y", "S"};
   
   for(int i = 0; i < ArraySize(timeframes); i++) {
      for(int j = 0; j < ArraySize(suffixes); j++) {
         string prefix = timeframes[i] + suffixes[j];
         ObjectsDeleteAll(0, prefix);
      }
   }
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // Clean up all objects created by this indicator
   CleanupObjects();
}

Breakaware, copyright2012©

FBS