Save as Pro_Institutional_Zones.mq5 in MQL5/Indicators. This version implements strict array-as-series handling to ensure the mathematical algorithms run perfectly in MT5's architecture.
Code: Select all
//+------------------------------------------------------------------+
//| Pro_Institutional_Zones.mq5 |
//| SMC Zones + Displacement Filter + Push Alerts |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//--- Professional Inputs
input string ___SMC_SETTINGS___ = "--- Zone Settings ---";
input int LeftLen = 20;
input int RightLen = 15;
input double DisplacementMult = 1.5;
input bool ShowBreakers = true;
input string ___STYLE___ = "--- Visual Settings ---";
input color ColorSup = clrMediumSeaGreen;
input color ColorRes = clrCrimson;
input color ColorBrkSup = clrDodgerBlue;
input color ColorBrkRes = clrDarkOrange;
input string ___ALERTS___ = "--- Alert Suite ---";
input bool UsePopUp = true;
input bool UsePush = false;
input bool UseEmail = false;
struct Zone {
string name;
double top;
double bot;
bool isSup;
bool isActive;
bool isBreaker;
};
Zone activeZones[];
datetime lastBarTime = 0;
int OnInit() {
ObjectsDeleteAll(0, "SMC_PRO_");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
ObjectsDeleteAll(0, "SMC_PRO_");
}
void TriggerAlert(string message) {
string prefix = "[" + Symbol() + " " + EnumToString((ENUM_TIMEFRAMES)Period()) + "] ";
if(UsePopUp) Alert(prefix + message);
if(UsePush) SendNotification(prefix + message);
if(UseEmail) SendMail("SMC Institutional Alert", prefix + message);
}
void DrawZone(string name, datetime t1, double p1, datetime t2, double p2, color clr) {
if(ObjectFind(0, name) < 0) {
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, p1, t2, p2);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
} else {
ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
}
}
bool HasDisplacement(int index, const double &o[], const double &c[]) {
double avgBody = 0;
int limit = ArraySize(o);
for(int i=1; i<=15; i++) {
if(index+i < limit) avgBody += MathAbs(o[index+i] - c[index+i]);
}
avgBody /= 15.0;
double moveAwayBody = MathAbs(o[index-1] - c[index-1]);
return (moveAwayBody > (avgBody * DisplacementMult));
}
bool IsPivotHigh(int index, const double &h[], const double &o[], const double &c[]) {
double val = h[index];
for(int i=1; i<=LeftLen; i++) if(index+i < ArraySize(h) && h[index+i] > val) return false;
for(int i=1; i<=RightLen; i++) if(index-i >= 0 && h[index-i] >= val) return false;
return HasDisplacement(index, o, c);
}
bool IsPivotLow(int index, const double &l[], const double &o[], const double &c[]) {
double val = l[index];
for(int i=1; i<=LeftLen; i++) if(index+i < ArraySize(l) && l[index+i] < val) return false;
for(int i=1; i<=RightLen; i++) if(index-i >= 0 && l[index-i] <= val) return false;
return HasDisplacement(index, o, c);
}
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[]) {
ArraySetAsSeries(time, true);
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
if(rates_total < LeftLen + RightLen + 15) return(0);
if(time[0] == lastBarTime) return(rates_total);
lastBarTime = time[0];
int limit = prev_calculated == 0 ? rates_total - LeftLen - 16 : MathMax(rates_total - prev_calculated, RightLen + 1);
datetime futureTime = time[0] + PeriodSeconds() * 50;
// 1. Process active zones & Garbage Collection
for(int i = ArraySize(activeZones) - 1; i >= 0; i--) {
if(activeZones[i].isActive) {
bool brokenUp = close[1] > activeZones[i].top;
bool brokenDn = close[1] < activeZones[i].bot;
if((activeZones[i].isSup && brokenDn) || (!activeZones[i].isSup && brokenUp)) {
activeZones[i].isActive = false;
if(!activeZones[i].isBreaker) {
TriggerAlert("Institutional POI Mitigated!");
}
if(ShowBreakers && !activeZones[i].isBreaker) {
DrawZone(activeZones[i].name, 0, activeZones[i].top, time[0], activeZones[i].bot, clrNONE);
int size = ArraySize(activeZones);
ArrayResize(activeZones, size + 1);
bool newIsSup = !activeZones[i].isSup;
activeZones[size].name = "SMC_PRO_BRK_" + IntegerToString(time[0]);
activeZones[size].top = activeZones[i].top;
activeZones[size].bot = activeZones[i].bot;
activeZones[size].isSup = newIsSup;
activeZones[size].isActive = true;
activeZones[size].isBreaker = true;
color clr = newIsSup ? ColorBrkSup : ColorBrkRes;
DrawZone(activeZones[size].name, time[1], activeZones[size].top, futureTime, activeZones[size].bot, clr);
} else {
ObjectDelete(0, activeZones[i].name); // Strict Memory Cleanup
}
} else {
ObjectSetInteger(0, activeZones[i].name, OBJPROP_TIME2, futureTime);
}
}
}
// 2. Scan for new Swing Points
for(int i = limit; i >= RightLen; i--) {
if(IsPivotHigh(i, high, open, close)) {
int size = ArraySize(activeZones);
ArrayResize(activeZones, size + 1);
activeZones[size].name = "SMC_PRO_RES_" + IntegerToString(time[i]);
activeZones[size].top = high[i];
activeZones[size].bot = MathMax(close[i], open[i]);
activeZones[size].isSup = false;
activeZones[size].isActive = true;
activeZones[size].isBreaker = false;
DrawZone(activeZones[size].name, time[i], activeZones[size].top, futureTime, activeZones[size].bot, ColorRes);
}
if(IsPivotLow(i, low, open, close)) {
int size = ArraySize(activeZones);
ArrayResize(activeZones, size + 1);
activeZones[size].name = "SMC_PRO_SUP_" + IntegerToString(time[i]);
activeZones[size].top = MathMin(close[i], open[i]);
activeZones[size].bot = low[i];
activeZones[size].isSup = true;
activeZones[size].isActive = true;
activeZones[size].isBreaker = false;
DrawZone(activeZones[size].name, time[i], activeZones[size].top, futureTime, activeZones[size].bot, ColorSup);
}
}
return(rates_total);
}