//+------------------------------------------------------------------+ //| EqualLiquidity.mqh | //| Copyright 2026, Concrete Goonies | //| https://concrete-goonies.web.app | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Concrete Goonies" #property link "https://concrete-goonies.web.app" #property version "1.00" #include "AbstractLiquidity.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class EqualLiquidity : public AbstractLiquidity { private: bool HasRequiredInternalSwingsForEqh(datetime leftIndex, datetime rightIndex) { return CountSwingHighsBetween(leftIndex, rightIndex) > configs.eqConfigs.minSwingCount; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool HasRequiredInternalSwingsForEql(datetime leftIndex, datetime rightIndex) { return CountSwingLowsBetween(leftIndex, rightIndex) > configs.eqConfigs.minSwingCount; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsCleanEQH(datetime leftTime, datetime rightTime, double pointAPrice, double tolerance) { if(leftTime >= rightTime) return false; // If any historical swing high between the two points exceeded the threshold, invalidate EQH for(int i = 0; i < state.m_swingHighs.Size(); i++) { TimePrice sh = state.m_swingHighs.At(i); if(sh.x > leftTime && sh.x < rightTime) { if(sh.y > pointAPrice + tolerance) return false; } } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool IsCleanEQL(datetime leftTime, datetime rightTime, double pointAPrice, double tolerance) { if(leftTime >= rightTime) return false; // If any historical swing low between the two points fell below the threshold, invalidate EQL for(int i = 0; i < state.m_swingLows.Size(); i++) { TimePrice sl = state.m_swingLows.At(i); if(sl.x > leftTime && sl.x < rightTime) { if(sl.y < pointAPrice - tolerance) return false; } } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ public: EqualLiquidity() {} ~EqualLiquidity() {} virtual string GetAnalyserName() override { return "EqualLiquidity"; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ virtual void OnSwing(TimePrice &swing, bool isHigh)override { double tolerance = configs.eqConfigs.tolerance; int maxTimeDiff = configs.eqConfigs.lookBack ; if(isHigh) { for(int i = 0; i < state.m_swingHighs.Size() - 1; i++) { TimePrice oldSwing = state.m_swingHighs.At(i); if(oldSwing.x == swing.x) break; int k = Bars(bar.symbol, bar.tf, oldSwing.x, swing.x); if(k > maxTimeDiff) continue; if(!NearPrice(oldSwing.y, swing.y, tolerance)) continue; if(!HasRequiredInternalSwingsForEqh(oldSwing.x, swing.x)) continue; if(!IsCleanEQH(oldSwing.x, swing.x, oldSwing.y, tolerance)) continue; string name = "EQH_" + IntegerToString((int)oldSwing.x) + "_" + IntegerToString((int)swing.x); // CreateTrendL(name, oldSwing.x, oldSwing.y, swing.x, swing.y, clrRed); EqualLiquidityEvent* e = new EqualLiquidityEvent(bar.symbol, bar.tf, BSL, oldSwing.x, oldSwing.y, swing.x, swing.y); eventBus.Push(e); } } else { for(int i = 0; i < state.m_swingLows.Size() - 1; i++) { TimePrice oldSwing = state.m_swingLows.At(i); if(oldSwing.x == swing.x) break; int k = Bars(bar.symbol, bar.tf, oldSwing.x, swing.x); if(k > maxTimeDiff) continue; if(!NearPrice(oldSwing.y, swing.y, tolerance)) continue; if(!HasRequiredInternalSwingsForEql(oldSwing.x, swing.x)) continue; if(!IsCleanEQL(oldSwing.x, swing.x, oldSwing.y, tolerance)) continue; string name = "EQL_" + IntegerToString((int)oldSwing.x) + "_" + IntegerToString((int)swing.x); // CreateTrendL(name, oldSwing.x, oldSwing.y, swing.x, swing.y, clrDodgerBlue); EqualLiquidityEvent* e = new EqualLiquidityEvent(bar.symbol, bar.tf, SSL, oldSwing.x, oldSwing.y, swing.x, swing.y); eventBus.Push(e); } } } }; //+------------------------------------------------------------------+