Warrior_EA/CustomIndicators/ADWyckoffSignificantBarInversion.mq5
AnimateDread 9464e52784 feat: embed compute DLLs as resources and extend Save/Load for training state
Add #resource directives for WarriorDML.dll and WarriorCPU.dll, and ExtractComputeDlls() to extract them on first run, enabling MQL Cloud Protector builds to ship DLLs without manual copy. Extend CNet::Save and Load with trainingComplete and indicatorParams arrays to persist AutoTune indicator param values.
2026-07-13 15:59:35 -04:00

375 lines
11 KiB
MQL5

//+------------------------------------------------------------------+
//| ADWyckoffSignificantBarInversion.mq5 |
//| Standalone significant-bar quality and movement inversion |
//+------------------------------------------------------------------+
#property copyright "AD Institutional Indicators"
#property link ""
#property version "1.00"
#property description "Wyckoff significant bar + control flip"
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots 5
#property indicator_label1 "SignificantBarQuality"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrGold
#property indicator_width1 1
#property indicator_label2 "BullishSignificantBar"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrLime
#property indicator_width2 1
#property indicator_label3 "BearishSignificantBar"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrRed
#property indicator_width3 1
#property indicator_label4 "BullishControlFlip"
#property indicator_type4 DRAW_LINE
#property indicator_color4 clrAqua
#property indicator_width4 1
#property indicator_label5 "BearishControlFlip"
#property indicator_type5 DRAW_LINE
#property indicator_color5 clrOrange
#property indicator_width5 1
input int InpLookback = 50;
input double InpRangeSignificant = 1.2;
input double InpVolumeHigh = 1.5;
input double InpATR = 0.5;
input int InpContextMode = 0; // 0=FixedBars, 1=Session
input int InpSessionType = 5; // 1..10
input int InpSessionCount = 1; // 1..20
double BufQuality[];
double BufBullSig[];
double BufBearSig[];
double BufBullFlip[];
double BufBearFlip[];
int ExtLookback;
double ExtRangeSignificant;
double ExtVolumeHigh;
double ExtATR;
int ExtContextMode;
int ExtSessionType;
int ExtSessionCount;
bool ExtHasBull = false;
bool ExtHasBear = false;
double ExtLastBullHigh = 0.0;
double ExtLastBullLow = 0.0;
double ExtLastBearHigh = 0.0;
double ExtLastBearLow = 0.0;
int IsPreviousSessionTypeMT5(const int sessionType)
{
return (sessionType >= 1 && sessionType <= 4);
}
int IsZigZagSessionTypeMT5(const int sessionType)
{
return (sessionType == 9 || sessionType == 10);
}
int MapBaseCalendarTypeMT5(const int sessionType)
{
if(sessionType == 1 || sessionType == 5) return 1;
if(sessionType == 2 || sessionType == 6) return 2;
if(sessionType == 3 || sessionType == 7) return 3;
if(sessionType == 4 || sessionType == 8) return 4;
return 1;
}
datetime DayStartMT5(const datetime t)
{
MqlDateTime dt;
TimeToStruct(t, dt);
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
return StructToTime(dt);
}
datetime AddMonthsSafeMT5(const datetime t, const int months)
{
MqlDateTime dt;
TimeToStruct(t, dt);
int m = dt.mon + months;
while(m > 12)
{
m -= 12;
dt.year++;
}
while(m < 1)
{
m += 12;
dt.year--;
}
dt.mon = m;
dt.day = 1;
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
return StructToTime(dt);
}
datetime ResolveCalendarContextStartMT5(const datetime currentTime)
{
int st = ExtSessionType;
int baseType = MapBaseCalendarTypeMT5(st);
datetime currentStart = DayStartMT5(currentTime);
if(baseType == 2)
{
MqlDateTime w;
TimeToStruct(currentStart, w);
int dow = w.day_of_week;
int back = (dow == 0) ? 6 : (dow - 1);
currentStart -= (datetime)(back * 86400);
}
else if(baseType == 3)
{
MqlDateTime m;
TimeToStruct(currentStart, m);
m.day = 1;
m.hour = 0;
m.min = 0;
m.sec = 0;
currentStart = StructToTime(m);
}
else if(baseType == 4)
{
MqlDateTime y;
TimeToStruct(currentStart, y);
y.mon = 1;
y.day = 1;
y.hour = 0;
y.min = 0;
y.sec = 0;
currentStart = StructToTime(y);
}
int shift = IsPreviousSessionTypeMT5(st) ? -ExtSessionCount : -(ExtSessionCount - 1);
if(baseType == 1)
return currentStart + (datetime)(shift * 86400);
if(baseType == 2)
return currentStart + (datetime)(shift * 7 * 86400);
if(baseType == 3)
return AddMonthsSafeMT5(currentStart, shift);
return AddMonthsSafeMT5(currentStart, shift * 12);
}
void ResetState()
{
ExtHasBull = false;
ExtHasBear = false;
ExtLastBullHigh = 0.0;
ExtLastBullLow = 0.0;
ExtLastBearHigh = 0.0;
ExtLastBearLow = 0.0;
}
void OnInit()
{
ExtLookback = (int)MathMax(5, MathMin(200, InpLookback));
ExtRangeSignificant = MathMax(0.5, MathMin(3.0, InpRangeSignificant));
ExtVolumeHigh = MathMax(0.5, MathMin(3.0, InpVolumeHigh));
ExtATR = MathMax(0.1, MathMin(3.0, InpATR));
ExtContextMode = (InpContextMode == 1) ? 1 : 0;
ExtSessionType = (int)MathMax(1, MathMin(10, InpSessionType));
ExtSessionCount = (int)MathMax(1, MathMin(20, InpSessionCount));
SetIndexBuffer(0, BufQuality, INDICATOR_DATA);
SetIndexBuffer(1, BufBullSig, INDICATOR_DATA);
SetIndexBuffer(2, BufBearSig, INDICATOR_DATA);
SetIndexBuffer(3, BufBullFlip, INDICATOR_DATA);
SetIndexBuffer(4, BufBearFlip, INDICATOR_DATA);
ArraySetAsSeries(BufQuality, true);
ArraySetAsSeries(BufBullSig, true);
ArraySetAsSeries(BufBearSig, true);
ArraySetAsSeries(BufBullFlip, true);
ArraySetAsSeries(BufBearFlip, true);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, ExtLookback);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, ExtLookback);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, ExtLookback);
PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, ExtLookback);
PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, ExtLookback);
IndicatorSetInteger(INDICATOR_DIGITS, 3);
IndicatorSetString(INDICATOR_SHORTNAME, "WYSB(" + IntegerToString(ExtLookback) + ")");
ResetState();
}
void OnDeinit(const int reason)
{
}
int ResolveMaxShift(const int i, const int rates_total, const datetime &time[])
{
int available = rates_total - 1 - i;
if(available <= 0)
return 0;
if(ExtContextMode != 1)
return MathMin(available, ExtLookback - 1);
if(IsZigZagSessionTypeMT5(ExtSessionType))
return MathMin(available, ExtLookback * ExtSessionCount - 1);
datetime start = ResolveCalendarContextStartMT5(time[i]);
int maxShift = 0;
for(int s = 0; s <= available; s++)
{
int idx = i + s;
if(time[idx] < start)
break;
maxShift = s;
}
return MathMax(0, maxShift);
}
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[])
{
if(rates_total < 20)
return 0;
ArraySetAsSeries(time, true);
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(tick_volume, true);
ResetState();
ArrayInitialize(BufQuality, 0.0);
ArrayInitialize(BufBullSig, 0.0);
ArrayInitialize(BufBearSig, 0.0);
ArrayInitialize(BufBullFlip, 0.0);
ArrayInitialize(BufBearFlip, 0.0);
int startBar = rates_total - 1;
for(int i = startBar; i >= 0 && !IsStopped(); i--)
{
int maxShift = ResolveMaxShift(i, rates_total, time);
if(maxShift < 1)
{
BufQuality[i] = 0.0;
continue;
}
double sumRange = 0.0;
double sumVol = 0.0;
int n = 0;
for(int s = 0; s <= maxShift; s++)
{
int idx = i + s;
double r = high[idx] - low[idx];
double v = MathMax((double)tick_volume[idx], 1.0);
sumRange += MathMax(r, 0.000001);
sumVol += v;
n++;
}
if(n <= 0)
continue;
double avgRange = sumRange / n;
double avgVol = sumVol / n;
if(avgRange <= 0.0 || avgVol <= 0.0)
continue;
double barRange = MathMax(high[i] - low[i], 0.000001);
double barVol = MathMax((double)tick_volume[i], 1.0);
double closePos = (close[i] - low[i]) / barRange;
if(closePos < 0.0) closePos = 0.0;
if(closePos > 1.0) closePos = 1.0;
double rangeRatio = barRange / avgRange;
double volRatio = barVol / avgVol;
int available = rates_total - 1 - i;
int atrN = MathMin(14, available);
if(atrN <= 0)
atrN = 1;
double sumTR = 0.0;
for(int t = 0; t < atrN; t++)
{
int idx = i + t;
double h = high[idx];
double l = low[idx];
double pc = (idx + 1 < rates_total) ? close[idx + 1] : close[idx];
double tr = MathMax(h - l, MathMax(MathAbs(h - pc), MathAbs(l - pc)));
sumTR += tr;
}
double atr = sumTR / atrN;
double atrRatio = barRange / MathMax(atr * ExtATR, 0.000001);
double fallbackResistance = (i + 1 < rates_total) ? high[i + 1] : high[i];
double fallbackSupport = (i + 1 < rates_total) ? low[i + 1] : low[i];
double priorResistance = ExtHasBear ? ExtLastBearHigh : fallbackResistance;
double priorSupport = ExtHasBull ? ExtLastBullLow : fallbackSupport;
bool bullStructuralBreak = (close[i] > priorResistance && high[i] > priorResistance);
bool bearStructuralBreak = (close[i] < priorSupport && low[i] < priorSupport);
bool bullSig = (rangeRatio >= ExtRangeSignificant && volRatio >= ExtVolumeHigh && closePos >= 0.70 && close[i] > open[i] && atrRatio >= 1.0 && bullStructuralBreak);
bool bearSig = (rangeRatio >= ExtRangeSignificant && volRatio >= ExtVolumeHigh && closePos <= 0.30 && close[i] < open[i] && atrRatio >= 1.0 && bearStructuralBreak);
double bullScore = ((rangeRatio / ExtRangeSignificant) + (volRatio / ExtVolumeHigh) + closePos) / 3.0;
double bearScore = ((rangeRatio / ExtRangeSignificant) + (volRatio / ExtVolumeHigh) + (1.0 - closePos)) / 3.0;
if(bullScore < 0.0) bullScore = 0.0;
if(bearScore < 0.0) bearScore = 0.0;
if(bullScore > 2.0) bullScore = 2.0;
if(bearScore > 2.0) bearScore = 2.0;
double quality = 0.0;
if(bullSig) quality = bullScore;
else if(bearSig) quality = -bearScore;
if(bullSig)
{
ExtHasBull = true;
ExtLastBullHigh = high[i];
ExtLastBullLow = low[i];
}
if(bearSig)
{
ExtHasBear = true;
ExtLastBearHigh = high[i];
ExtLastBearLow = low[i];
}
double prevClose = (i + 1 < rates_total) ? close[i + 1] : close[i];
bool bullFlip = (ExtHasBear && prevClose <= ExtLastBearHigh && close[i] > ExtLastBearHigh);
bool bearFlip = (ExtHasBull && prevClose >= ExtLastBullLow && close[i] < ExtLastBullLow);
// Normalize: guard against NaN/Inf from edge-case inputs
if(!MathIsValidNumber(quality)) quality = 0.0;
BufQuality[i] = quality;
BufBullSig[i] = bullSig ? 1.0 : 0.0;
BufBearSig[i] = bearSig ? 1.0 : 0.0;
BufBullFlip[i] = bullFlip ? 1.0 : 0.0;
BufBearFlip[i] = bearFlip ? 1.0 : 0.0;
}
return rates_total;
}