//+------------------------------------------------------------------+ //| CommonHelper.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include #include #include "Indicator.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class StructureLevels { public: TimePrice protectedHigh; TimePrice protectedLow; TimePrice bullNoBos; TimePrice bearNoBos; TimePrice bullConfirmedLow; TimePrice bearConfirmedHigh; int direction; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CommonHelper { private: ENUM_TIMEFRAMES timeframe; string symbol; CBarsbydate bar; CNewBar newBar; public: CommonHelper(); ~CommonHelper(); Swing swing; void UpdateBars(ENUM_TIMEFRAMES timefrme, string symbl, bool useMonths = false, int monthsBack = 100) { timeframe = timefrme; symbol = symbl; bar.UpdateFromMonthsBack(symbol, timeframe, monthsBack, useMonths); swing.UpdateFromMonthsBack(monthsBack, useMonths); } int OnInit(string s, ENUM_TIMEFRAMES tf, int lr = 3, int look = 1) { symbol = s; timeframe = tf; bar.SetAsSeries(false); swing.SetAsSeries(false); if(swing.Init("Fractal", s, tf, lr, look) == -1) { Print("Error creating Swing Indicator: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } void SetAsSeries(bool isAsSeries) { bar.SetAsSeries(isAsSeries); } bool OnNewBar() { return newBar.CheckNewBar(symbol, timeframe); } int Bars() { return bar.Size(); } int LastBar() { return bar.Size() - 1; } bool HasBars(int count) { return bar.Size() >= count; } bool IsValidIndex(int index = 0) { return (index >= 0 && index < bar.Size()); } datetime TimeAt(int index = 0) { return bar.Time(index); } double OpenAt(int index = 0) { return bar.Open(index); } double HighAt(int index = 0) { return bar.High(index); } double LowAt(int index = 0) { return bar.Low(index); } double CloseAt(int index = 0) { return bar.Close(index); } double RangeAt(int index = 0) { return bar.High(index) - bar.Low(index); } double BodyTop(int index = 0) { return MathMax(bar.Open(index), bar.Close(index)); } double BodyBottom(int index = 0) { return MathMin(bar.Open(index), bar.Close(index)); } double BodySize(int index = 0) { return MathAbs(bar.Close(index) - bar.Open(index)); } double UpperWick(int index = 0) { return bar.High(index) - BodyTop(index); } double LowerWick(int index = 0) { return BodyBottom(index) - bar.Low(index); } double MidPrice(double a, double b) { return (a + b) * 0.5; } double CandleMid(int index = 0) { return MidPrice(bar.High(index), bar.Low(index)); } int TimeIndex(datetime t) { if(t <= 0) return -1; return bar.IndexByTime(t); } //======================== // Price helpers //======================== double PointValue() { return bar.Point(); } double PointsToPrice(int points) { return points * PointValue(); } bool NearPrice(double a, double b, int tolerancePoints) { return MathAbs(a - b) <= PointsToPrice(tolerancePoints); } //======================== // Break helpers //======================== bool BreakAboveHigh(int refIndex = 0, int barIndex = 0) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Close(barIndex) > bar.High(refIndex); } bool BreakBelowLow(int refIndex = 0, int barIndex = 0) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Close(barIndex) < bar.Low(refIndex); } bool WickAboveHigh(int refIndex = 0, int barIndex = 0) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.High(barIndex) > bar.High(refIndex); } bool WickBelowLow(int refIndex = 0, int barIndex = 0) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Low(barIndex) < bar.Low(refIndex); } //======================== // Candle state //======================== bool IsBullishCandle(int index = 0) { if(!IsValidIndex(index)) return false; return bar.Close(index) > bar.Open(index); } bool IsBearishCandle(int index = 0) { if(!IsValidIndex(index)) return false; return bar.Close(index) < bar.Open(index); } bool IsDojiCandle(int index = 0, double maxBodyToRange = 0.10) { if(!IsValidIndex(index)) return false; double range = RangeAt(index); if(range <= 0.0) return false; return (BodySize(index) / range) <= maxBodyToRange; } bool IsSellToBuyCandle(int index = 0, double wickToBodyRatio = 1.2) { if(!IsValidIndex(index)) return false; double body = BodySize(index); if(body <= 0.0) body = _Point; return LowerWick(index) >= body * wickToBodyRatio; } bool IsBuyToSellCandle(int index = 0, double wickToBodyRatio = 1.2) { if(!IsValidIndex(index)) return false; double body = BodySize(index); if(body <= 0.0) body = _Point; return UpperWick(index) >= body * wickToBodyRatio; } //======================== // Extremes in range //======================== int FindLowestIndex(int fromIndex = 0, int toIndex = 0, bool inclusive = false) { if(fromIndex > toIndex) { int temp = fromIndex; fromIndex = toIndex; toIndex = temp; } int start = (inclusive ? fromIndex : fromIndex + 1); int end = (inclusive ? toIndex : toIndex - 1); if(start > end) return -1; int lowestIndex = -1; for(int i = start; i <= end; i++) { if(!IsValidIndex(i)) continue; if(lowestIndex == -1 || bar.Low(i) < bar.Low(lowestIndex)) lowestIndex = i; } return lowestIndex; } int FindHighestIndex(int fromIndex = 0, int toIndex = 0, bool inclusive = false) { if(fromIndex > toIndex) { int temp = fromIndex; fromIndex = toIndex; toIndex = temp; } int start = (inclusive ? fromIndex : fromIndex + 1); int end = (inclusive ? toIndex : toIndex - 1); if(start > end) return -1; int highestIndex = -1; for(int i = start; i <= end; i++) { if(!IsValidIndex(i)) continue; if(highestIndex == -1 || bar.High(i) > bar.High(highestIndex)) highestIndex = i; } return highestIndex; } //======================== // Swing helpers //======================== bool IsValidSwingValue(double value) { return swing.IsValidValue(value); } bool IsSwingHigh(int index = 0) { if(!IsValidIndex(index)) return false; return swing.IsValidValue(swing.FullHigh(index)); } bool IsSwingLow(int index = 0) { if(!IsValidIndex(index)) return false; return swing.IsValidValue(swing.FullLow(index)); } double SwingHigh(int index = 0) { if(!IsValidIndex(index)) return 0; if(!swing.IsValidValue(swing.FullHigh(index))) return 0; return swing.High(index); } double SwingLow(int index = 0) { if(!IsValidIndex(index)) return false; if(!swing.IsValidValue(swing.FullLow(index))) return false; return swing.Low(index); } int PrevSwingHighIndex(int anchor = 0, int n = 1) { int found = -1; for(int i = anchor; i >= 0; i--) { if(IsSwingHigh(i)) { found++; if(found == n) return i; } } return -1; } bool HasValidTime(datetime t) { return t > 0; } bool IsOlderTime(datetime olderTime, datetime newerTime) { if(!HasValidTime(olderTime) || !HasValidTime(newerTime)) return false; return olderTime < newerTime; } bool IsOlderOrSameTime(datetime olderTime, datetime newerTime) { if(!HasValidTime(olderTime) || !HasValidTime(newerTime)) return false; return olderTime <= newerTime; } int CountClosedBarsAfterTime(datetime anchorTime, datetime currentTime) { if(!IsOlderTime(anchorTime, currentTime)) return -1; int anchorIndex = TimeIndex(anchorTime); int currentIndex = TimeIndex(currentTime); if(!IsValidIndex(anchorIndex) || !IsValidIndex(currentIndex)) return -1; if(currentIndex <= anchorIndex) return -1; return (currentIndex - anchorIndex - 1); } //+------------------------------------------------------------------+ //| | //+---------------------------j---------------------------------------+ bool AnchorIsMatureForBreak(int minCandles, datetime anchorTime, datetime currentTime) { int closedBars = MathAbs(TimeIndex(anchorTime) - TimeIndex(currentTime)); Print(closedBars); return closedBars >= minCandles; } void SyncIndexFromTime(TimePrice &tp) { if(tp.x == 0) { tp.i = -1; return; } tp.i = TimeIndex(tp.x); } int TimeframeMinutes() { return PeriodSeconds(timeframe) / 60; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CommonHelper::CommonHelper() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CommonHelper::~CommonHelper() { } //+------------------------------------------------------------------+