205 lines
6.2 KiB
MQL5
205 lines
6.2 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| AbstractLiquidity.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 "../AbstractAnalyser.mqh"
| |
| |
struct EqConfigs {
| |
double tolerance;
| |
int minSwingCount;
| |
int lookBack;
| |
bool allowAverage;
| |
};
| |
| |
struct StructConfigs {
| |
double maxPercent;
| |
double minPercent;
| |
int lookBack;
| |
};
| |
| |
struct MomShiftConfigs {
| |
double maxPercent;
| |
double minPercent;
| |
double tolerance; // Dedicated tolerance for MomShift
| |
int maxSwingCount; // Dynamic swing limit for pullbacks
| |
};
| |
| |
struct LiquidityLvl {
| |
TimePrice a;
| |
TimePrice b;
| |
};
| |
| |
| |
struct LiquidityConfigs {
| |
EqConfigs eqConfigs;
| |
StructConfigs structConfigs;
| |
MomShiftConfigs momShiftConfigs;
| |
};
| |
| |
struct ZigzagSwing {
| |
TimePrice point;
| |
bool isHigh;
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class LiqState {
| |
public:
| |
string symbol;
| |
ENUM_TIMEFRAMES tf;
| |
| |
CArrayList<TimePrice>* m_swingHighs;
| |
CArrayList<TimePrice>* m_swingLows;
| |
CArrayList<ZigzagSwing>* m_swings;
| |
| |
// Pullback tracking (per state)
| |
double pullbackTargetPrice;
| |
double pullbackUpperLevel; // s3.y + tolerance
| |
double pullbackLowerLevel; // s1.y - tolerance
| |
int pullbackSwingCount;
| |
bool pullbackActive;
| |
| |
datetime lastMomShiftS1Time; // prevents duplicate fires
| |
double momShiftInvalidationBuffer; // optional, or use configs
| |
| |
| |
// Inside LiqState:
| |
double protectedLowPrice;
| |
double protectedHighPrice;
| |
datetime protectedLowTime;
| |
datetime protectedHighTime;
| |
bool hasProtectedLow;
| |
bool hasProtectedHigh;
| |
| |
TimePrice pullbackLastPoint;
| |
| |
LiqState() {
| |
m_swingHighs = new CArrayList<TimePrice>();
| |
m_swingLows = new CArrayList<TimePrice>();
| |
m_swings = new CArrayList<ZigzagSwing>();
| |
| |
pullbackTargetPrice = 0.0;
| |
pullbackUpperLevel = 0.0;
| |
pullbackLowerLevel = 0.0;
| |
pullbackSwingCount = 0;
| |
pullbackActive = false;
| |
| |
protectedLowPrice = 0.0;
| |
protectedHighPrice = 0.0;
| |
protectedLowTime = 0;
| |
protectedHighTime = 0;
| |
hasProtectedLow = false;
| |
hasProtectedHigh = false;
| |
}
| |
| |
~LiqState() {
| |
delete m_swingHighs;
| |
delete m_swingLows;
| |
delete m_swings;
| |
}
| |
| |
bool HasSwingAt(datetime time, bool isHigh) {
| |
if(isHigh) {
| |
for(int i = 0; i < m_swingHighs.Size(); i++)
| |
if(m_swingHighs.At(i).x == time)
| |
return true;
| |
} else {
| |
for(int i = 0; i < m_swingLows.Size(); i++)
| |
if(m_swingLows.At(i).x == time)
| |
return true;
| |
}
| |
| |
return false;
| |
}
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class AbstractLiquidity : public AbstractAnalyser {
| |
| |
protected:
| |
Indicator* ind;
| |
TimeFrameDataSeries* bar;
| |
LiqState* state;
| |
LiquidityConfigs configs;
| |
| |
bool NearPrice(double a, double b, double tol) {
| |
return MathAbs(a - b) <= tol;
| |
}
| |
| |
void CreateTrendL(string name, datetime t1, double p1, datetime t2, double p2, color clr) {
| |
long chart_id = ChartID();
| |
bool created = ObjectCreate(chart_id, name, OBJ_TREND, 0, t1, p1, t2, p2);
| |
| |
if(created) {
| |
ObjectSetInteger(chart_id, name, OBJPROP_COLOR, clr);
| |
ObjectSetInteger(chart_id, name, OBJPROP_RAY_RIGHT, false);
| |
ObjectSetInteger(chart_id, name, OBJPROP_WIDTH, 2);
| |
}
| |
}
| |
| |
void CreateTrendL(string name, const TimePrice &p1, const TimePrice &p2, color clr) {
| |
CreateTrendL(name, p1.x, p1.y, p2.x, p2.y, clr);
| |
}
| |
| |
int CountSwingHighsBetween(datetime leftTime, datetime rightTime) {
| |
int count = 0;
| |
for(int i = 0; i < state.m_swingLows.Size(); i++) {
| |
TimePrice sl = state.m_swingLows.At(i);
| |
if(sl.x > leftTime && sl.x < rightTime)
| |
count++;
| |
}
| |
return count;
| |
}
| |
| |
//--- Checks intermediate swings using absolute datetime (bypasses 5-bar limit)
| |
int CountSwingLowsBetween(datetime leftTime, datetime rightTime) {
| |
int count = 0;
| |
for(int i = 0; i < state.m_swingHighs.Size(); i++) {
| |
TimePrice sh = state.m_swingHighs.At(i);
| |
if(sh.x > leftTime && sh.x < rightTime)
| |
count++;
| |
}
| |
return count ;
| |
}
| |
bool HasSwingAt(datetime time, bool isHigh) {
| |
if(isHigh) {
| |
for(int i = 0; i < state.m_swingHighs.Size(); i++) {
| |
if(state.m_swingHighs.At(i).x == time) {
| |
| |
return true;
| |
}
| |
| |
}
| |
} else {
| |
for(int i = 0; i < state.m_swingLows.Size(); i++) {
| |
if(state.m_swingLows.At(i).x == time)
| |
return true;
| |
}
| |
}
| |
return false;
| |
}
| |
public:
| |
AbstractLiquidity() {}
| |
~AbstractLiquidity() {}
| |
| |
virtual void ProcessBar(TimeFrameDataSeries* series, int i) override { }
| |
virtual void ProcessBar(LiqState* s, Indicator* _ind, TimeFrameDataSeries* series, int i) {
| |
ind = _ind;
| |
bar = series;
| |
state = s;
| |
| |
}
| |
| |
void SetConfigs(LiquidityConfigs &c) {
| |
configs = c;
| |
}
| |
virtual void OnSwing(TimePrice &swing, bool isHigh) = 0;
| |
| |
};
| |
//+------------------------------------------------------------------+
|