Warrior_EA/CustomIndicators/ADWyckoffFailedStructure.mq5
AnimateDread e62c710d6f fix: correct array orientation and PReLU gradient backprop in hidden layers
- Ensure `tick_volume` array is set as series in ADShorteningOfThrust.mq5 to prevent future-data leak in volume calculations.
- Ensure `open` array is set as series in ADWyckoffFailedStructure.mq5 to prevent future-data leak in structure detection.
- Add missing PReLU gradient scaling (multiply by 0.01 for negative outputs) in CPU_CalcHiddenGradient and DirectML shader to match expected derivative behavior across all backends.
2026-07-17 23:21:12 -04:00

399 lines
No EOL
14 KiB
MQL5

//+------------------------------------------------------------------+
//| ADWyckoffFailedStructure.mq5|
//| Wyckoff Failed Structure / Break family |
//+------------------------------------------------------------------+
#property copyright "AD Institutional Indicators"
#property link ""
#property version "1.00"
#property description "Wyckoff Failed Structure — bullish/bearish structural failure"
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots 5
#property indicator_minimum -2.5
#property indicator_maximum 2.5
#property indicator_level1 0
#property indicator_label1 "Value"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrWhite
#property indicator_width1 1
#property indicator_label2 "BullishStructuralFailure"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrLime
#property indicator_width2 1
#property indicator_label3 "BearishStructuralFailure"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrRed
#property indicator_width3 1
#property indicator_label4 "FailedAccumulation"
#property indicator_type4 DRAW_LINE
#property indicator_color4 clrAqua
#property indicator_width4 1
#property indicator_label5 "FailedDistribution"
#property indicator_type5 DRAW_LINE
#property indicator_color5 clrOrange
#property indicator_width5 1
input int InpLookbackPeriod = 50;
input int InpZigZagStrength = 3;
input double InpVolumeClimaxMultiplier = 2.5;
input double InpVolumeHighMultiplier = 1.5;
input double InpRangeClimaxMultiplier = 1.8;
input double InpRangeSignificantMult = 1.2;
input double InpSTVolumeRatio = 0.6;
input double InpATRMultiplier = 0.5;
input int InpContextMode = 0;
input int InpSessionType = 5;
input int InpSessionCount = 1;
double ExtValueBuffer[];
double ExtBullishFailureBuffer[];
double ExtBearishFailureBuffer[];
double ExtFailedAccumulationBuffer[];
double ExtFailedDistributionBuffer[];
double H[];
double L[];
double C[];
double V[];
datetime T[];
int ExtLookbackPeriod;
int ExtZigZagStrength;
double ExtVolumeClimaxMultiplier;
double ExtVolumeHighMultiplier;
double ExtRangeClimaxMultiplier;
double ExtRangeSignificantMult;
double ExtSTVolumeRatio;
double ExtATRMultiplier;
int ExtContextMode;
int ExtSessionType;
int ExtSessionCount;
double lastBullHigh = 0.0;
double lastBullLow = 0.0;
double lastBearHigh = 0.0;
double lastBearLow = 0.0;
int ClampInt(const int value, const int minValue, const int maxValue) { return (int)MathMax(minValue, MathMin(maxValue, value)); }
double ClampDouble(const double value, const double minValue, const double maxValue) { return MathMax(minValue, MathMin(maxValue, value)); }
bool IsPreviousSessionTypeMT5(const int sessionType) { return (sessionType >= 1 && sessionType <= 4); }
bool 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()
{
lastBullHigh = 0.0;
lastBullLow = 0.0;
lastBearHigh = 0.0;
lastBearLow = 0.0;
ArrayInitialize(ExtValueBuffer, 0);
ArrayInitialize(ExtBullishFailureBuffer, 0);
ArrayInitialize(ExtBearishFailureBuffer, 0);
ArrayInitialize(ExtFailedAccumulationBuffer, 0);
ArrayInitialize(ExtFailedDistributionBuffer, 0);
}
void SetOutputs(const int outIndex, const double value, const double bull, const double bear, const double failedAccum, const double failedDist)
{
ExtValueBuffer[outIndex] = value;
ExtBullishFailureBuffer[outIndex] = bull;
ExtBearishFailureBuffer[outIndex] = bear;
ExtFailedAccumulationBuffer[outIndex] = failedAccum;
ExtFailedDistributionBuffer[outIndex] = failedDist;
}
int ResolveMaxShift(const int currentBar, const int rates_total, const datetime &time[])
{
int lookback = MathMax(5, MathMin(200, ExtLookbackPeriod));
int available = currentBar;
if(ExtContextMode != 1)
return MathMin(available, lookback - 1);
if(IsZigZagSessionTypeMT5(ExtSessionType))
return MathMin(available, (lookback * ExtSessionCount) - 1);
datetime start = ResolveCalendarContextStartMT5(time[currentBar]);
int lower = 0;
for(int j = 0; j <= currentBar; j++)
{
if(time[j] >= start)
{
lower = j;
break;
}
}
return MathMax(0, currentBar - lower);
}
double ComputeATRChrono(const int i, const int period)
{
int available = MathMin(period, i);
if(available <= 0)
return H[i] - L[i];
double sum = 0.0;
for(int k = 0; k < available; k++)
{
int idx = i - k;
double hi = H[idx];
double lo = L[idx];
double prevClose = (idx > 0) ? C[idx - 1] : hi;
double tr = MathMax(hi - lo, MathMax(MathAbs(hi - prevClose), MathAbs(lo - prevClose)));
sum += tr;
}
return sum / available;
}
int OnInit()
{
ExtLookbackPeriod = ClampInt(InpLookbackPeriod, 5, 200);
ExtZigZagStrength = ClampInt(InpZigZagStrength, 1, 10);
ExtVolumeClimaxMultiplier = ClampDouble(InpVolumeClimaxMultiplier, 1.0, 5.0);
ExtVolumeHighMultiplier = ClampDouble(InpVolumeHighMultiplier, 0.5, 3.0);
ExtRangeClimaxMultiplier = ClampDouble(InpRangeClimaxMultiplier, 1.0, 5.0);
ExtRangeSignificantMult = ClampDouble(InpRangeSignificantMult, 0.5, 3.0);
ExtSTVolumeRatio = ClampDouble(InpSTVolumeRatio, 0.1, 1.0);
ExtATRMultiplier = ClampDouble(InpATRMultiplier, 0.1, 3.0);
ExtContextMode = (InpContextMode == 1) ? 1 : 0;
ExtSessionType = ClampInt(InpSessionType, 1, 10);
ExtSessionCount = ClampInt(InpSessionCount, 1, 20);
SetIndexBuffer(0, ExtValueBuffer, INDICATOR_DATA);
SetIndexBuffer(1, ExtBullishFailureBuffer, INDICATOR_DATA);
SetIndexBuffer(2, ExtBearishFailureBuffer, INDICATOR_DATA);
SetIndexBuffer(3, ExtFailedAccumulationBuffer, INDICATOR_DATA);
SetIndexBuffer(4, ExtFailedDistributionBuffer, INDICATOR_DATA);
ArraySetAsSeries(ExtValueBuffer, true);
ArraySetAsSeries(ExtBullishFailureBuffer, true);
ArraySetAsSeries(ExtBearishFailureBuffer, true);
ArraySetAsSeries(ExtFailedAccumulationBuffer, true);
ArraySetAsSeries(ExtFailedDistributionBuffer, true);
IndicatorSetInteger(INDICATOR_DIGITS, 3);
IndicatorSetString(INDICATOR_SHORTNAME,
"WFS(" + IntegerToString(ExtLookbackPeriod) + "," + IntegerToString(ExtZigZagStrength) + "," +
DoubleToString(ExtRangeSignificantMult, 2) + "," + DoubleToString(ExtVolumeHighMultiplier, 2) + ")");
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, ExtLookbackPeriod);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, ExtLookbackPeriod);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, ExtLookbackPeriod);
PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, ExtLookbackPeriod);
PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, ExtLookbackPeriod);
ResetState();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {}
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 < ExtLookbackPeriod + 2)
return(0);
ArraySetAsSeries(time, true);
// open was missing here: it's read directly (not through the chronological H/L/C/V/T cache
// below) at `open[si]` using the series-space index `si`, while open itself stayed in its
// default chronological orientation - a genuine future-data leak (worst at the oldest processed
// bar, which read the NEWEST bar's open) into bullSig/bearSig and, from there, every output
// buffer (Value/BullishStructuralFailure/BearishStructuralFailure/FailedAccumulation/
// FailedDistribution). Flipping it here makes it consistent with every other array below.
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(tick_volume, true);
ArraySetAsSeries(volume, true);
ArrayResize(H, rates_total);
ArrayResize(L, rates_total);
ArrayResize(C, rates_total);
ArrayResize(V, rates_total);
ArrayResize(T, rates_total);
ResetState();
for(int chrono = 0; chrono < rates_total; chrono++)
{
int si = rates_total - 1 - chrono;
H[chrono] = high[si];
L[chrono] = low[si];
C[chrono] = close[si];
V[chrono] = (double)MathMax(tick_volume[si], 1);
T[chrono] = time[si];
}
for(int chrono = 0; chrono < rates_total && !IsStopped(); chrono++)
{
int si = rates_total - 1 - chrono;
int maxShift = ResolveMaxShift(chrono, rates_total, T);
double sumRange = 0.0;
double sumVolume = 0.0;
int n = 0;
int from = MathMax(0, chrono - maxShift);
for(int k = from; k <= chrono; k++)
{
sumRange += MathMax(H[k] - L[k], 0.000001);
sumVolume += MathMax(V[k], 1.0);
n++;
}
if(n <= 0)
{
SetOutputs(si, 0.0, 0.0, 0.0, 0.0, 0.0);
continue;
}
double avgRange = sumRange / n;
double avgVolume = sumVolume / n;
if(!MathIsValidNumber(avgRange) || !MathIsValidNumber(avgVolume) || avgRange <= 0 || avgVolume <= 0)
{
SetOutputs(si, 0.0, 0.0, 0.0, 0.0, 0.0);
continue;
}
double hi = H[chrono];
double lo = L[chrono];
double op = open[si];
double cl = C[chrono];
double vol = MathMax(V[chrono], 1.0);
double prevClose = (chrono > 0) ? C[chrono - 1] : cl;
double barRange = MathMax(hi - lo, 0.000001);
double closePos = (cl - lo) / barRange;
closePos = ClampDouble(closePos, 0.0, 1.0);
double rangeRatio = barRange / avgRange;
double volRatio = vol / avgVolume;
double rangeGate = ClampDouble(ExtRangeSignificantMult, 0.5, 3.0);
double volGate = ClampDouble(ExtVolumeHighMultiplier, 0.5, 3.0);
double climaxRangeGate = ClampDouble(ExtRangeClimaxMultiplier, 0.5, 5.0);
double climaxVolGate = ClampDouble(ExtVolumeClimaxMultiplier, 0.5, 5.0);
double atrNow = ComputeATRChrono(chrono, 14);
double atrRatio = barRange / MathMax(atrNow * ExtATRMultiplier, 0.000001);
double breakBuffer = MathMax(atrNow * 0.05, barRange * 0.02) * MathMax(1.0, (double)ExtZigZagStrength);
double stGate = ExtSTVolumeRatio;
bool bullSig = rangeRatio >= rangeGate && volRatio >= volGate && closePos >= 0.70 && cl > op && atrRatio >= 1.0 && volRatio >= stGate;
bool bearSig = rangeRatio >= rangeGate && volRatio >= volGate && closePos <= 0.30 && cl < op && atrRatio >= 1.0 && volRatio >= stGate;
bool climaxBull = rangeRatio >= climaxRangeGate && volRatio >= climaxVolGate && closePos >= 0.80 && cl > op;
bool climaxBear = rangeRatio >= climaxRangeGate && volRatio >= climaxVolGate && closePos <= 0.20 && cl < op;
double qualityBase = MathMax(rangeRatio / MathMax(rangeGate, 0.000001), volRatio / MathMax(volGate, 0.000001));
double quality = ClampDouble(qualityBase + ((climaxBull || climaxBear) ? 0.5 : 0.0), 0.0, 2.5);
double resistance = (lastBearHigh > 0.0) ? lastBearHigh : 0.0;
double support = (lastBullLow > 0.0) ? lastBullLow : 0.0;
bool bullishContext = cl > op || closePos >= 0.55;
bool bearishContext = cl < op || closePos <= 0.45;
bool bullishFailure = false;
bool bearishFailure = false;
if(bearishContext && bullSig && resistance > 0.0)
bullishFailure = (prevClose <= resistance + breakBuffer && cl > resistance + breakBuffer);
if(bullishContext && bearSig && support > 0.0)
bearishFailure = (prevClose >= support - breakBuffer && cl < support - breakBuffer);
double signedValue = 0.0;
if(bullishFailure)
signedValue = quality;
else if(bearishFailure)
signedValue = -quality;
if(bullSig)
{
lastBullHigh = hi;
lastBullLow = lo;
}
if(bearSig)
{
lastBearHigh = hi;
lastBearLow = lo;
}
// Normalize: guard against NaN/Inf from edge-case inputs
if(!MathIsValidNumber(signedValue)) signedValue = 0.0;
SetOutputs(si, signedValue, bullishFailure ? 1.0 : 0.0, bearishFailure ? 1.0 : 0.0, bearishFailure ? 1.0 : 0.0, bullishFailure ? 1.0 : 0.0);
}
return(rates_total);
}
//+------------------------------------------------------------------+