DiagnoseADBuffers.mq5 reports, per indicator per buffer per chart, the share of EMPTY_VALUE, the share of exact zeros, the single most common value share, and the standard deviation - flagging NEVER-FILLED / ALL-ZERO / CONSTANT / NEAR-CONSTANT. It answers the question that has to be settled BEFORE any "this feature carries no signal" verdict: is the buffer computing anything at all. A constant column is a code fault, not a weak feature - a real but weak signal still varies. FIRST RUN, 40,000 H1 bars x 5 charts: ADWyckoffEventStream buffers 12 and 13 (Reaccumulation, Redistribution) are identically zero with sd exactly 0 on ALL FIVE charts. Tracing it gives a proof rather than a suspicion: line 465 accumulation ranges open only under tr<=0, calling OpenRange(+1, bi, tr) line 392 continuation needs (dir>0 && trendAtOpen>0), and trendAtOpen IS that tr so the reaccumulation branch is unreachable by construction, and redistribution is unreachable symmetrically. gRIsContinuation can never be true. The conceptual error is one Trend(bi) reading serving two different questions - the climax gate asks about the trend the climax TERMINATES, reaccumulation asks about a larger trend the range sits INSIDE. Different horizons; collapsing them makes the conditions mutually exclusive. This corrects my own earlier reading. I reported the block as "dead at every parameter set" from a 41-candidate sweep on two bands; no parameter can make an unreachable branch fire, so that search was never a test of the concept. The feature set is unchanged - the operator is having the suite reviewed for correctness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
195 lines
9.1 KiB
MQL5
195 lines
9.1 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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.");
|
|
}
|
|
//+------------------------------------------------------------------+
|