//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+