//+------------------------------------------------------------------+ //| DiagnoseADBuffers.mq5 | //| AnimateDread | //| | //| BUFFER HEALTH for the five AD/Wyckoff custom indicators, at the | //| SHIPPED defaults, on every fleet chart. | //| | //| WHY: a feature block that scores zero can be uninformative OR | //| BROKEN, and those two look identical from downstream. The screen | //| said ADWyckoffEventStream clears 0 of 16 columns on all five | //| charts, and a 41-candidate parameter sweep found no setting that | //| changed it - but a search over the parameters of a function that | //| is not computing what it claims fails at EVERY point in the | //| space, which is exactly that pattern. The feature-health report | //| separately noted EventStream is where every CONSTANT column | //| appeared, and a constant column is a bug signature: a real but | //| weak feature still VARIES. | //| | //| This does not judge whether a block PAYS. It answers the prior | //| question - is each buffer being filled with something that moves | //| - so a correctness review starts from measurements rather than | //| from reading code cold. | //| | //| Flags raised per buffer: | //| NEVER-FILLED every value EMPTY_VALUE / non-finite | //| ALL-ZERO finite everywhere but never non-zero | //| CONSTANT exactly one distinct finite value | //| NEAR-CONSTANT one value covers >99% of bars | //| SPARSE non-zero on <0.5% of bars (may be correct for an | //| event flag - read it beside the event's expected | //| frequency, not as a fault on its own) | //+------------------------------------------------------------------+ #property copyright "AnimateDread" #property link "https://www.mql5.com" #property version "1.00" input string InpSymbols = "EURUSD,USDJPY,NAS100,XAUUSD,XTIUSD"; // Charts to check input int InpBars = 40000; // Bars of H1 history //+------------------------------------------------------------------+ void DiagnoseOne(const string sym, const string indName, const int buffers, MqlParam &mp[]) { int h = IndicatorCreate(sym, PERIOD_H1, IND_CUSTOM, ArraySize(mp), mp); if(h == INVALID_HANDLE) { PrintFormat(" %-34s %-8s IndicatorCreate FAILED (error %d)", indName, sym, GetLastError()); return; } //--- Let the terminal finish calculating before reading, or an incomplete series reads as a fault. int calc = 0; for(int t = 0; t < 300; t++) { calc = BarsCalculated(h); if(calc >= InpBars) break; Sleep(100); } if(calc < InpBars) PrintFormat(" %-34s %-8s NOTE: BarsCalculated=%d of %d requested", indName, sym, calc, InpBars); for(int c = 0; c < buffers; c++) { double v[]; ArraySetAsSeries(v, true); int got = CopyBuffer(h, c, 0, InpBars, v); if(got <= 0) { PrintFormat(" %-34s %-8s buf%-2d COPY FAILED (%d, error %d)", indName, sym, c, got, GetLastError()); continue; } int nFinite = 0, nZero = 0, nEmpty = 0; double mn = 0, mx = 0, sum = 0, sum2 = 0; bool first = true; for(int i = 0; i < got; i++) { double x = v[i]; if(!MathIsValidNumber(x) || x == EMPTY_VALUE) { nEmpty++; continue; } nFinite++; if(x == 0.0) nZero++; sum += x; sum2 += x * x; if(first) { mn = x; mx = x; first = false; } else { if(x < mn) mn = x; if(x > mx) mx = x; } } string flag = "ok"; double sd = 0.0, mean = 0.0, modeShare = 0.0; if(nFinite == 0) flag = "NEVER-FILLED"; else { mean = sum / nFinite; double var = sum2 / nFinite - mean * mean; sd = (var > 1e-18) ? MathSqrt(var) : 0.0; //--- Share of the single most common value, via a sorted copy of the finite entries. double s[]; ArrayResize(s, nFinite); int w = 0; for(int i = 0; i < got; i++) { double x = v[i]; if(MathIsValidNumber(x) && x != EMPTY_VALUE) s[w++] = x; } ArraySort(s); int best = 1, run = 1; for(int i = 1; i < nFinite; i++) { if(s[i] == s[i - 1]) run++; else { if(run > best) best = run; run = 1; } } if(run > best) best = run; modeShare = (double)best / nFinite; if(nZero == nFinite) flag = "ALL-ZERO"; else if(mn == mx) flag = "CONSTANT"; else if(modeShare > 0.99) flag = "NEAR-CONSTANT"; else if((double)(nFinite - nZero) / got < 0.005) flag = "SPARSE"; } PrintFormat(" %-34s %-8s buf%-2d empty %5.1f%% zero %5.1f%% modeShare %5.1f%% sd %-12.6g [%s]", indName, sym, c, 100.0 * nEmpty / got, 100.0 * nZero / got, 100.0 * modeShare, sd, flag); } //--- Released in the same iteration that created it. IndicatorRelease(h); } //+------------------------------------------------------------------+ void PushInt(MqlParam &mp[], int &n, const long v) { mp[n].type = TYPE_INT; mp[n].integer_value = v; n++; } void PushDbl(MqlParam &mp[], int &n, const double v) { mp[n].type = TYPE_DOUBLE; mp[n].double_value = v; n++; } //+------------------------------------------------------------------+ void OnStart(void) { Print("=================================================================="); Print("AD/WYCKOFF BUFFER HEALTH - shipped defaults, per chart, per buffer."); Print("Answers 'is this buffer computing anything', NOT 'does it pay'."); Print("A CONSTANT or ALL-ZERO buffer is a code fault, not a weak feature:"); Print("a real but weak signal still varies."); Print("=================================================================="); string syms[]; int ns = StringSplit(InpSymbols, ',', syms); for(int s = 0; s < ns; s++) { string sym = syms[s]; StringTrimLeft(sym); StringTrimRight(sym); if(sym == "") continue; Print(""); PrintFormat("---------------- %s ----------------", sym); MqlParam mp[]; int n; //--- ADCumulativeDelta: 10 inputs, 6 buffers ArrayResize(mp, 11); n = 0; mp[n].type = TYPE_STRING; mp[n].string_value = "ADCumulativeDelta"; n++; PushInt(mp, n, 50); PushDbl(mp, n, 2.5); PushDbl(mp, n, 1.5); PushDbl(mp, n, 1.8); PushDbl(mp, n, 1.2); PushDbl(mp, n, 0.6); PushDbl(mp, n, 0.5); PushInt(mp, n, 0); PushInt(mp, n, 5); PushInt(mp, n, 1); DiagnoseOne(sym, "ADCumulativeDelta", 6, mp); //--- ADShorteningOfThrust: 6 inputs, 4 buffers ArrayResize(mp, 7); n = 0; mp[n].type = TYPE_STRING; mp[n].string_value = "ADShorteningOfThrust"; n++; PushInt(mp, n, 30); PushInt(mp, n, 3); PushDbl(mp, n, 0.30); PushInt(mp, n, 0); PushInt(mp, n, 5); PushInt(mp, n, 1); DiagnoseOne(sym, "ADShorteningOfThrust", 4, mp); //--- ADWyckoffEventStream: 14 inputs, 14 buffers ArrayResize(mp, 15); n = 0; mp[n].type = TYPE_STRING; mp[n].string_value = "ADWyckoffEventStream"; n++; PushInt(mp, n, 50); PushInt(mp, n, 3); PushDbl(mp, n, 2.5); PushDbl(mp, n, 1.5); PushDbl(mp, n, 1.8); PushDbl(mp, n, 1.2); PushDbl(mp, n, 0.6); PushDbl(mp, n, 0.5); PushInt(mp, n, 0); PushInt(mp, n, 5); PushInt(mp, n, 1); PushDbl(mp, n, 0.5); PushDbl(mp, n, 1.0); PushInt(mp, n, 200); DiagnoseOne(sym, "ADWyckoffEventStream", 14, mp); //--- ADWyckoffFailedStructure: 11 inputs, 5 buffers ArrayResize(mp, 12); n = 0; mp[n].type = TYPE_STRING; mp[n].string_value = "ADWyckoffFailedStructure"; n++; PushInt(mp, n, 50); PushInt(mp, n, 3); PushDbl(mp, n, 2.5); PushDbl(mp, n, 1.5); PushDbl(mp, n, 1.8); PushDbl(mp, n, 1.2); PushDbl(mp, n, 0.6); PushDbl(mp, n, 0.5); PushInt(mp, n, 0); PushInt(mp, n, 5); PushInt(mp, n, 1); DiagnoseOne(sym, "ADWyckoffFailedStructure", 5, mp); //--- ADWyckoffSignificantBarInversion: 7 inputs, 5 buffers ArrayResize(mp, 8); n = 0; mp[n].type = TYPE_STRING; mp[n].string_value = "ADWyckoffSignificantBarInversion"; n++; PushInt(mp, n, 50); PushDbl(mp, n, 1.2); PushDbl(mp, n, 1.5); PushDbl(mp, n, 0.5); PushInt(mp, n, 0); PushInt(mp, n, 5); PushInt(mp, n, 1); DiagnoseOne(sym, "ADWyckoffSignificantBarInversion", 5, mp); } Print(""); Print("Done. NEVER-FILLED / ALL-ZERO / CONSTANT on a buffer that should carry an event"); Print("is a defect to fix in the indicator, not a reason to drop the feature block."); } //+------------------------------------------------------------------+