//+------------------------------------------------------------------+ //| AbstractChapter.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" #property strict #include "Inducators.mqh" class CAbstractChapter { protected: ENUM_TIMEFRAMES timeframe; string symbol; CBarsbydate bar; Swing swing; CNewBar newBar; CChartDebug debug; protected: //======================== // Life cycle //======================== bool OnNewBar() { return newBar.CheckNewBar(symbol, timeframe); } void UpdateBars(int monthsBack = 3) { bar.UpdateFromMonthsBack(symbol, timeframe, monthsBack); } //======================== // Bar info //======================== int Bars() { return bar.Size(); } bool HasBars(int count) { return bar.Size() >= count; } bool IsValidIndex(int index) { return (index >= 0 && index < bar.Size()); } datetime TimeAt(int index) { return bar.Time(index); } double OpenAt(int index) { return bar.Open(index); } double HighAt(int index) { return bar.High(index); } double LowAt(int index) { return bar.Low(index); } double CloseAt(int index) { return bar.Close(index); } double RangeAt(int index) { return bar.High(index) - bar.Low(index); } double BodyTop(int index) { return MathMax(bar.Open(index), bar.Close(index)); } double BodyBottom(int index) { return MathMin(bar.Open(index), bar.Close(index)); } double BodySize(int index) { return MathAbs(bar.Close(index) - bar.Open(index)); } double UpperWick(int index) { return bar.High(index) - BodyTop(index); } double LowerWick(int index) { return BodyBottom(index) - bar.Low(index); } double MidPrice(double a, double b) { return (a + b) * 0.5; } double CandleMid(int index) { return MidPrice(bar.High(index), bar.Low(index)); } //======================== // 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); } //======================== // Swing helpers //======================== bool IsValidSwingValue(double value) { return swing.IsValidValue(value); } bool IsSwingHigh(int index) { if(!IsValidIndex(index)) return false; return swing.IsValidValue(swing.HighAt(index)); } bool IsSwingLow(int index) { if(!IsValidIndex(index)) return false; return swing.IsValidValue(swing.LowAt(index)); } int PrevSwingHighIndex(int anchor, int n = 1) { int found = -1; for(int i = anchor; i >= 0; i--) { if(IsSwingHigh(i)) { found++; if(found == n) return i; } } return -1; } int PrevSwingLowIndex(int anchor, int n = 1) { int found = -1; for(int i = anchor; i >= 0; i--) { if(IsSwingLow(i)) { found++; if(found == n) return i; } } return -1; } int NextSwingHighIndex(int anchor, int n = 1) { int found = 0; for(int i = anchor; i < bar.Size(); i++) { if(IsSwingHigh(i)) { found++; if(found == n) return i; } } return -1; } int NextSwingLowIndex(int anchor, int n = 1) { int found = 0; for(int i = anchor; i < bar.Size(); i++) { if(IsSwingLow(i)) { found++; if(found == n) return i; } } return -1; } //======================== // Break helpers //======================== bool BreakAboveHigh(int refIndex, int barIndex) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Close(barIndex) > bar.High(refIndex); } bool BreakBelowLow(int refIndex, int barIndex) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Close(barIndex) < bar.Low(refIndex); } bool WickAboveHigh(int refIndex, int barIndex) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.High(barIndex) > bar.High(refIndex); } bool WickBelowLow(int refIndex, int barIndex) { if(!IsValidIndex(refIndex) || !IsValidIndex(barIndex)) return false; return bar.Low(barIndex) < bar.Low(refIndex); } //======================== // Candle state //======================== bool IsBullishCandle(int index) { if(!IsValidIndex(index)) return false; return bar.Close(index) > bar.Open(index); } bool IsBearishCandle(int index) { if(!IsValidIndex(index)) return false; return bar.Close(index) < bar.Open(index); } bool IsDojiCandle(int index, 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, 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, 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, int toIndex, 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, int toIndex, 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; } //======================== // Zone helpers for Chapter 4 //======================== double ZoneMid(double top, double bottom) { return (top + bottom) * 0.5; } double ZoneHeight(double top, double bottom) { return MathAbs(top - bottom); } bool PriceTouchesZone(int index, double top, double bottom) { if(!IsValidIndex(index)) return false; double zTop = MathMax(top, bottom); double zBot = MathMin(top, bottom); return bar.Low(index) <= zTop && bar.High(index) >= zBot; } bool PriceClosesInsideZone(int index, double top, double bottom) { if(!IsValidIndex(index)) return false; double zTop = MathMax(top, bottom); double zBot = MathMin(top, bottom); double c = bar.Close(index); return c <= zTop && c >= zBot; } bool PriceClosesBeyondSupply(int index, double top) { if(!IsValidIndex(index)) return false; return bar.Close(index) > top; } bool PriceClosesBeyondDemand(int index, double bottom) { if(!IsValidIndex(index)) return false; return bar.Close(index) < bottom; } //======================== // Drawing helpers //======================== void DrawLevel(string key, int fromIndex, int toIndex, double price, string text, color col = clrChartreuse, int style = 1) { if(!IsValidIndex(fromIndex) || !IsValidIndex(toIndex)) return; debug.LineWithCenteredText( key, bar.Time(fromIndex), price, bar.Time(toIndex), price, text, style, col ); } void DrawZone(string key, int leftIndex, int rightIndex, double top, double bottom, string text, color col, double insetFrac = 0.06, int fontSize = 6, bool drawOnTop = false) { if(!IsValidIndex(leftIndex) || !IsValidIndex(rightIndex)) return; debug.RectWithCenteredText( symbol, key, bar.Time(leftIndex), top, bar.Time(rightIndex), bottom, text, col, insetFrac, fontSize, drawOnTop ); } public: CAbstractChapter(void) { timeframe = PERIOD_CURRENT; symbol = _Symbol; } virtual ~CAbstractChapter() {} virtual int OnInit(string s, ENUM_TIMEFRAMES tf_, int lr = 3, int look = 1) { timeframe = tf_; symbol = s; debug.Init(0, __FILE__, true); if(swing.Init("Fractal", s, tf_, lr, look) == -1) { Print("Error creating Swing Indicator: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } virtual void OnCalculate() {} }; //+------------------------------------------------------------------+