The primary issues I noticed are:
Missing variables for anti-cluttering (MinBarsBetweenLabels, MinPriceDistanceFactor)
Multiple function redefinitions
Issues with TimeToStruct function parameters
Undeclared identifiers ('day', 'day_of_week', 'mon')
below is the code
------------------------------------------------------------------------------------------------------------________________________________________________________________________
#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)
// 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;
}
//+------------------------------------------------------------------+
//| 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);
// Check if close to other labels
bool isHighCluttered = IsLabelCluttered(time, high, tracker);
bool isLowCluttered = IsLabelCluttered(time, low, tracker);
// Create labels with anti-cluttering adjustments
double highYOffset = 0;
double lowYOffset = 0;
if(isHighCluttered) {
// Increase vertical counter and apply offset for high label
tracker.verticalCounter++;
highYOffset = LabelVerticalSpacing * tracker.verticalCounter;
}
if(isLowCluttered) {
// Increase vertical counter and apply offset for low label
tracker.verticalCounter++;
lowYOffset = -LabelVerticalSpacing * tracker.verticalCounter;
}
// Create text objects with offsets
CreateLabel(labelNameHigh, time, high + highYOffset, highLabel, OpenRangeColor);
CreateLabel(labelNameLow, time, low + lowYOffset, 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
// Check time proximity (within 10 bars)
int barsBetween = (int)MathAbs((time - tracker.lastLabelTime) / PeriodSeconds());
if(barsBetween > 10) return false;
// Check price proximity (within 5 average true range units)
double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 1);
double priceDiff = MathAbs(price - tracker.lastLabelPrice);
return priceDiff < (5 * atr);
}
//+------------------------------------------------------------------+
//| 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);
// Check for cluttering
bool isCluttered = IsLabelCluttered(time, price, tracker);
double yOffset = 0;
// Apply anti-cluttering if needed
if(isCluttered) {
// Determine offset direction and apply
int direction = isPositive ? 1 : -1;
tracker.verticalCounter++;
yOffset = direction * LabelVerticalSpacing * tracker.verticalCounter;
}
// 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 with offset
color labelColor = isPositive ? PositiveColor : NegativeColor;
CreateLabel(objName, time, price + yOffset, text, labelColor);
// Update tracker
tracker.lastLabelTime = time;
tracker.lastLabelPrice = price;
tracker.lastLabelDirection = isPositive ? 1 : -1;
// Reset vertical counter if we've gone past a threshold
if(tracker.verticalCounter > 5) {
tracker.verticalCounter = 0;
}
}
//+------------------------------------------------------------------+
//| Add a wave point to the array |
//+------------------------------------------------------------------+
void AddWavePoint(WavePoint &points[], WavePoint &point) {
int size = ArraySize(points);
ArrayResize(points, size + 1);
points[size] = point;
}
//+------------------------------------------------------------------+
//| Get the price of the last target point with given sign |
//+------------------------------------------------------------------+
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive) {
for(int i = ArraySize(points) - 1; i >= 0; i--) {
if(points[i].isPositive == positive && points[i].type == 0) {
return points[i].price;
}
}
return 0; // Not found
}
//+------------------------------------------------------------------+
//| Get the price of the last flip point with given sign |
//+------------------------------------------------------------------+
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive) {
for(int i = ArraySize(points) - 1; i >= 0; i--) {
if(points[i].isPositive == positive && points[i].type == 1) {
return points[i].price;
}
}
return 0; // Not found
}
//+------------------------------------------------------------------+
//| Create a text label on the chart with improved positioning |
//+------------------------------------------------------------------+
void CreateLabel(string name, datetime time, double price, string text, color clr) {
// Check if object already exists to avoid duplicate labels
if(ObjectFind(0, name) < 0) {
// Create text object
ObjectCreate(0, name, OBJ_TEXT, 0, time, price);
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 to move text slightly away from the price point
ObjectSetInteger(0, name, OBJPROP_XOFFSET, LabelHorizontalOffset);
}
}
//+------------------------------------------------------------------+
//| Determine the current trading session based on time |
//+------------------------------------------------------------------+
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time) {
MqlDateTime dt;
TimeToStruct(time, dt);
int hour = dt.hour;
// Check if in London session
if(hour >= LondonOpenHour && hour < LondonCloseHour) {
return SESSION_LONDON;
}
// Check if in New York session
if(hour >= NewYorkOpenHour && hour < NewYorkCloseHour) {
return SESSION_NEWYORK;
}
// Check if in Sydney session (may span across midnight)
if(SydneyOpenHour > SydneyCloseHour) {
if(hour >= SydneyOpenHour || hour < SydneyCloseHour) {
return SESSION_SYDNEY;
}
} else {
if(hour >= SydneyOpenHour && hour < SydneyCloseHour) {
return SESSION_SYDNEY;
}
}
// Check if in Asia session (may span across midnight)
if(AsiaOpenHour > AsiaCloseHour) {
if(hour >= AsiaOpenHour || hour < AsiaCloseHour) {
return SESSION_ASIA;
}
} else {
if(hour >= AsiaOpenHour && hour < AsiaCloseHour) {
return SESSION_ASIA;
}
}
// Default to London if no match
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 all objects created by this indicator |
//+------------------------------------------------------------------+
void CleanupObjects() {
ObjectsDeleteAll(0, "Target_");
ObjectsDeleteAll(0, "Flip_");
ObjectsDeleteAll(0, "High_");
ObjectsDeleteAll(0, "Low_");
ObjectsDeleteAll(0, "DayOpenRange");
ObjectsDeleteAll(0, "WeekOpenRange");
ObjectsDeleteAll(0, "MonthOpenRange");
ObjectsDeleteAll(0, "YearOpenRange");
ObjectsDeleteAll(0, "SessionOpenRange");
}
//+------------------------------------------------------------------+
//| Deinitialize the indicator |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
// Clean up all objects created by this indicator
CleanupObjects();
}
//+------------------------------------------------------------------+
//| Enhanced function for checking label cluttering |
//+------------------------------------------------------------------+
bool IsLabelCluttered(datetime time, double price, const PointTracker &tracker) {
if(tracker.lastLabelTime == 0) return false; // First label
double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 1);
// Check time proximity (within X bars)
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) {
double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 1);
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);
double atr = iATR(_Symbol, PERIOD_CURRENT, 14, 1);
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
}
//+------------------------------------------------------------------+
//| Create open range labels with improved 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);
// Check if close to other labels and find optimal positions
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 with the last created label
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
}
//+------------------------------------------------------------------+
//| Create wave point label with improved 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 = labelTime;
tracker.lastLabelPrice = labelPrice;
tracker.lastLabelDirection = direction;
}
//+------------------------------------------------------------------+
//| Create a text label on the chart with improved positioning |
//+------------------------------------------------------------------+
void CreateLabel(string name, datetime time, double price, string text, color clr) {
// Check if object already exists to avoid duplicate labels
if(ObjectFind(0, name) < 0) {
// Create text object
ObjectCreate(0, name, OBJ_TEXT, 0, time, price);
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 to move text slightly away from the price point
ObjectSetInteger(0, name, OBJPROP_XOFFSET, LabelHorizontalOffset);
// Make sure the label is on top of chart
ObjectSetInteger(0, name, OBJPROP_ZORDER, 100);
}
}
//+------------------------------------------------------------------+
//| Update extremes and process wave points with improved 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;
}
}
//+------------------------------------------------------------------+
//| Determine the current trading session based on time |
//+------------------------------------------------------------------+
ENUM_SESSION_TYPE DetermineCurrentSession(datetime time) {
MqlDateTime dt;
TimeToStruct(time, dt);
int hour = dt.hour;
// Check if in London session
if(hour >= LondonOpenHour && hour < LondonCloseHour) {
return SESSION_LONDON;
}
// Check if in New York session
if(hour >= NewYorkOpenHour && hour < NewYorkCloseHour) {
return SESSION_NEWYORK;
}
// Check if in Sydney session (may span across midnight)
if(SydneyOpenHour > SydneyCloseHour) {
if(hour >= SydneyOpenHour || hour < SydneyCloseHour) {
return SESSION_SYDNEY;
}
} else {
if(hour >= SydneyOpenHour && hour < SydneyCloseHour) {
return SESSION_SYDNEY;
}
}
// Check if in Asia session (may span across midnight)
if(AsiaOpenHour > AsiaCloseHour) {
if(hour >= AsiaOpenHour || hour < AsiaCloseHour) {
return SESSION_ASIA;
}
} else {
if(hour >= AsiaOpenHour && hour < AsiaCloseHour) {
return SESSION_ASIA;
}
}
// Default to London if no match
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";
}
}
//+------------------------------------------------------------------+
//| Add a wave point to the array |
//+------------------------------------------------------------------+
void AddWavePoint(WavePoint &points[], WavePoint &point) {
int size = ArraySize(points);
ArrayResize(points, size + 1);
points[size] = point;
}
//+------------------------------------------------------------------+
//| Get the price of the last target point with given sign |
//+------------------------------------------------------------------+
double GetLastTargetPriceWithSign(WavePoint &points[], bool positive) {
for(int i = ArraySize(points) - 1; i >= 0; i--) {
if(points[i].isPositive == positive && points[i].type == 0) {
return points[i].price;
}
}
return 0; // Not found
}
//+------------------------------------------------------------------+
//| Get the price of the last flip point with given sign |
//+------------------------------------------------------------------+
double GetLastFlipPriceWithSign(WavePoint &points[], bool positive) {
for(int i = ArraySize(points) - 1; i >= 0; i--) {
if(points[i].isPositive == positive && points[i].type == 1) {
return points[i].price;
}
}
return 0; // Not found
}
Breakaware, copyright2012©
No comments:
Post a Comment
Express Yourself, Be Gentle...