refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Warrior_EA |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| |
|
|
|
|
|
//| The MI evidence screen - "does this feature vector predict this |
|
|
|
|
|
//| label at all", answered without training, topology or |
|
2026-08-24 21:01:08 -04:00
|
|
|
//| convergence, and the label-alignment lookahead scan. |
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#ifndef WARRIOR_AIBASE_FEATURESCREEN_MQH
|
|
|
|
|
#define WARRIOR_AIBASE_FEATURESCREEN_MQH
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| SPLIT OUT OF AutoTune.mqh (2026-08-23): that file was two |
|
|
|
|
|
//| responsibilities - SEARCH (TuneIndicatorsByFilter, coordinate- |
|
|
|
|
|
//| descent over indicator settings) and MEASUREMENT (this file, which |
|
|
|
|
|
//| never mutates an indicator parameter). They share the MI engine |
|
|
|
|
|
//| that stays in AutoTune.mqh (FeatureColumnMI/BuildMiSample/ |
|
|
|
|
|
//| ScoreMiSample) because BOTH consume it - a genuine shared |
|
|
|
|
|
//| dependency, not a reason to keep the two responsibilities in one |
|
|
|
|
|
//| file. TuneIndicatorsAndTrain(), the entry point that decides which |
|
|
|
|
|
//| of the two branches a given model runs this process, also stays in |
|
|
|
|
|
//| AutoTune.mqh: it is the coordinator, not a member of either side. |
|
|
|
|
|
//| |
|
|
|
|
|
//| Still body-only method definitions of CExpertSignalAIBase, exactly |
|
|
|
|
|
//| like every other Expert\AIBase\*.mqh file - MQL5 has no partial |
|
|
|
|
|
//| classes, so this split is a FILE-organisation move (legibility, |
|
|
|
|
|
//| SRP-per-file) rather than a coupling reduction. See |
|
|
|
|
|
//| project_oop_module_pattern for what a REAL decoupling of this |
|
|
|
|
|
//| domain would need (a view + adapter, MQL5's single-inheritance |
|
|
|
|
|
//| tax) - not attempted here because nothing downstream yet needs one.|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| "Do these features predict this label at all?" - answered |
|
|
|
|
|
//| without training, topology or convergence, so unlike every |
|
|
|
|
|
//| accuracy number in this codebase it cannot be confounded by an |
|
|
|
|
|
//| optimizer or an objective. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CExpertSignalAIBase::ReportFeatureLabelInformation(void)
|
|
|
|
|
{
|
feat(pool,mi): one feature layout fleet-wide, and the keep-screen stops self-disabling on a cold start
TWO CHANGES, BOTH RETRAIN-FORCING BY INTENT.
1. SP500 was training alone, and one alt-data column was the reason.
The alt block's width joins the model fingerprint, and the pool reader only
adopts peer rows whose fingerprint and width match. The exporter gives each
instrument the series that apply to it - FX 15 columns, metals/oil 14, SP500
13 - so the fleet ran as three incompatible pools:
EURUSD/USDJPY/USDCAD adopt ~57-60k peer rows each
XAUUSD/XTIUSD adopt 6.4k / 20.3k
SP500 "EVERY peer file was REJECTED, so this chart is
training alone" - 0 rows
SP500 therefore trained on 2279 independent observations against a 600-wide
input with its first layer floored at 16, printing its own "expect
overfitting" warning. It is the one chart with no pool and the worst
capacity ratio in the fleet by a factor of three.
Fresh models now pin ALTDATA_FLEET_COLUMNS - the 12-column intersection -
instead of their own file header. An existing model still adopts its .cfg
pin, so this re-keys nothing that is already trained.
Intersection rather than union: filling an absent series with its median
makes that column constant per instrument, which lets a pooled model
identify the source instrument and stop learning the shared mechanism. It
is also 6 columns narrower. Cost is six columns whose retained information
is UNMEASURED - the keep-screen reports a bitmask nothing has mapped back
to names.
2. The MI keep-screen disabled itself for the whole run on any cold start.
ReportFeatureLabelInformation set m_miReportDone on ENTRY. On a cold start
the label cache is allocated before it is filled, so BuildMiSample finds no
row carrying a resolved label and returns 0 - a sixth exit, and the only
one the 8c1266d instrumentation did not cover, which is why it printed
nothing. observed then stayed -1, the permutation loop never iterated, and
the report emitted "-1.00000 nats over 0 permutations" beside a plausible
"strongest single feature 0.05979" that was a STALE m_miBestColumn from an
earlier scoring call. The first ensemble member propagated the latch to
g_ensembleChartMiReportDone and silenced every member on the chart.
The flag now latches only once a measurement exists. A short sample is
reported as a deferral naming the two numbers that identify it (cached bars
vs bars carrying a resolved label) and retried, up to
MI_REPORT_MAX_ATTEMPTS.
Build tag -> fleet-pool-v1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:41:14 -04:00
|
|
|
//--- THE LATCH IS AT THE END, NOT HERE, AND THAT WAS A BUG WORTH A SESSION. It used to be set on
|
|
|
|
|
//--- entry, so a COLD start - where the label cache is allocated before it is filled, BuildMiSample
|
|
|
|
|
//--- finds no row carrying a label, and returns 0 - disabled the screen for the whole run on the
|
|
|
|
|
//--- first attempt. The first ensemble member then propagated it to g_ensembleChartMiReportDone and
|
|
|
|
|
//--- silenced every other member on the chart. Warm starts never showed it.
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
//--- PERMUTATION TEST, done properly.
|
|
|
|
|
double cols[];
|
|
|
|
|
int labels[];
|
|
|
|
|
int nSample = BuildMiSample(cols, labels);
|
feat(pool,mi): one feature layout fleet-wide, and the keep-screen stops self-disabling on a cold start
TWO CHANGES, BOTH RETRAIN-FORCING BY INTENT.
1. SP500 was training alone, and one alt-data column was the reason.
The alt block's width joins the model fingerprint, and the pool reader only
adopts peer rows whose fingerprint and width match. The exporter gives each
instrument the series that apply to it - FX 15 columns, metals/oil 14, SP500
13 - so the fleet ran as three incompatible pools:
EURUSD/USDJPY/USDCAD adopt ~57-60k peer rows each
XAUUSD/XTIUSD adopt 6.4k / 20.3k
SP500 "EVERY peer file was REJECTED, so this chart is
training alone" - 0 rows
SP500 therefore trained on 2279 independent observations against a 600-wide
input with its first layer floored at 16, printing its own "expect
overfitting" warning. It is the one chart with no pool and the worst
capacity ratio in the fleet by a factor of three.
Fresh models now pin ALTDATA_FLEET_COLUMNS - the 12-column intersection -
instead of their own file header. An existing model still adopts its .cfg
pin, so this re-keys nothing that is already trained.
Intersection rather than union: filling an absent series with its median
makes that column constant per instrument, which lets a pooled model
identify the source instrument and stop learning the shared mechanism. It
is also 6 columns narrower. Cost is six columns whose retained information
is UNMEASURED - the keep-screen reports a bitmask nothing has mapped back
to names.
2. The MI keep-screen disabled itself for the whole run on any cold start.
ReportFeatureLabelInformation set m_miReportDone on ENTRY. On a cold start
the label cache is allocated before it is filled, so BuildMiSample finds no
row carrying a resolved label and returns 0 - a sixth exit, and the only
one the 8c1266d instrumentation did not cover, which is why it printed
nothing. observed then stayed -1, the permutation loop never iterated, and
the report emitted "-1.00000 nats over 0 permutations" beside a plausible
"strongest single feature 0.05979" that was a STALE m_miBestColumn from an
earlier scoring call. The first ensemble member propagated the latch to
g_ensembleChartMiReportDone and silenced every member on the chart.
The flag now latches only once a measurement exists. A short sample is
reported as a deferral naming the two numbers that identify it (cached bars
vs bars carrying a resolved label) and retried, up to
MI_REPORT_MAX_ATTEMPTS.
Build tag -> fleet-pool-v1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:41:14 -04:00
|
|
|
//--- NOT ENOUGH SAMPLE IS NOT A MEASUREMENT. Reporting it as one printed "-1.00000 nats over 0
|
|
|
|
|
//--- permutations" beside a plausible-looking "strongest single feature 0.05979" - which was a
|
|
|
|
|
//--- STALE m_miBestColumn left by an earlier scoring call, not a number measured here. Say what
|
|
|
|
|
//--- happened, keep the flag clear so the next era retries, and print nothing that reads as data.
|
|
|
|
|
if(nSample < MI_MIN_SAMPLES)
|
|
|
|
|
{
|
|
|
|
|
m_miReportAttempts++;
|
|
|
|
|
bool giveUp = (m_miReportAttempts >= MI_REPORT_MAX_ATTEMPTS);
|
|
|
|
|
if(giveUp)
|
|
|
|
|
m_miReportDone = true;
|
|
|
|
|
Print(ID + StringFormat(": feature keep-screen DEFERRED - the usable sample holds %d row(s)"
|
|
|
|
|
" and %d are required (label cache %d bars, %d of them carrying a"
|
|
|
|
|
" resolved label). This is the cold-start ordering case: the cache is"
|
|
|
|
|
" allocated before it is filled. Attempt %d of %d%s",
|
|
|
|
|
nSample, MI_MIN_SAMPLES, m_labelCacheBars, LabelCacheResolvedCount(),
|
|
|
|
|
m_miReportAttempts, MI_REPORT_MAX_ATTEMPTS,
|
|
|
|
|
giveUp ? " - GIVING UP for this run; no keep-screen will be measured."
|
|
|
|
|
: " - retrying next era."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
double observed = ScoreMiSample(cols, labels, nSample, false);
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
double signalBestCol = m_miBestColumn;
|
|
|
|
|
double labelEntropy = m_miLabelEntropy;
|
feat(features): per-column MI keep-screen (report only)
Step 1 of the prune, stopping deliberately short of pruning - two blockers make
an immediate mask the wrong move, and this is the measurement that decides
whether pruning is worth doing at all.
WHY NOT PRUNE YET:
* the screen runs with cross-asset ABSENT - its own log line says the numbers
"describe a NARROWER vector than training will use". A mask built from it
would have no evidence either way about the cross-asset block.
* a per-chart mask FRAGMENTS THE POOL. The mask must participate in the
fingerprint, and the pool only accepts peers with an identical feature
layout. Pooling is currently the only thing keeping the FX trio off the
capacity floor - the three pool-poor charts (SP500, XAUUSD, XTIUSD) are
exactly the three still floored. Six per-chart masks = six pool groups of
one, and pruning could cost more capacity than it buys.
WHAT THIS ADDS: the per-column MI was always computed inside ScoreMiSample and
thrown away except for the sum and the max. It is retained now, and the same
permutation draws that build the headline null also accumulate a PER-COLUMN null,
which is what a per-column p-value needs - distinct from the null-of-the-max,
which answers the single family-wise question "is the strongest column real".
Selection uses Benjamini-Hochberg at q=0.10, NOT the family-wise bar. FWER
controls the chance of one false positive, which is right for a verdict and far
too conservative for selection - it would discard every genuinely weak-but-useful
feature. BH bounds the expected SHARE of kept columns that are noise, which is
what a feature set cares about.
The report prints the decision in capacity units: columns kept, the resulting
input width, and the first-layer budget before and after against the 16-wide
floor. 3 of 52 is not a feature set; 45 of 52 is not worth a fingerprint re-key.
The cross-asset caveat prints itself when it applies.
Context that makes this worth doing at all: under the pivot-event label the MI
screen now reads "above the noise floor - a real association" - mean 4x the null
(p=0.005), strongest column 7.7x the null-max, excess 0.80% of label entropy,
against 1.3x / 1.15x / ~0.1% under the old label. The noise-floor verdict that
closed several earlier directions was a property of the OLD label.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 15:43:59 -04:00
|
|
|
//--- OBSERVED PER-COLUMN MI, copied before the null loop overwrites m_miColumn on every draw.
|
|
|
|
|
double obsCol[];
|
|
|
|
|
int colN = (observed >= 0.0) ? ArraySize(m_miColumn) : 0;
|
|
|
|
|
int colAtLeast[];
|
|
|
|
|
if(colN > 0)
|
|
|
|
|
{
|
|
|
|
|
ArrayResize(obsCol, colN);
|
|
|
|
|
ArrayResize(colAtLeast, colN);
|
|
|
|
|
ArrayInitialize(colAtLeast, 0);
|
|
|
|
|
for(int f = 0; f < colN; f++)
|
|
|
|
|
obsCol[f] = m_miColumn[f];
|
|
|
|
|
}
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
double floorSum = 0.0, floorSumSq = 0.0, floorBestColSum = 0.0;
|
|
|
|
|
int draws = 0, atLeastMean = 0, atLeastBestCol = 0;
|
|
|
|
|
uint tPerm = GetTickCount();
|
|
|
|
|
for(int s = 0; observed >= 0.0 && s < MI_NOISE_PERMUTATIONS; s++)
|
|
|
|
|
{
|
|
|
|
|
//--- A few hundred full MI scorings, and nothing below can start until they finish. Abandon the
|
|
|
|
|
//--- test outright rather than truncate it: fewer draws does not make a smaller null, it makes a
|
2026-08-24 21:01:08 -04:00
|
|
|
//--- WRONG one (p shifts toward significance), and this null's spread prices the lookahead margin below.
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
if(ShutdownRequested())
|
|
|
|
|
{
|
|
|
|
|
draws = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-08-24 21:01:08 -04:00
|
|
|
double sc = ScoreMiSample(cols, labels, nSample, true);
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
if(sc < 0.0)
|
|
|
|
|
continue;
|
|
|
|
|
floorSum += sc;
|
|
|
|
|
floorSumSq += sc * sc;
|
|
|
|
|
floorBestColSum += m_miBestColumn;
|
|
|
|
|
if(sc >= observed)
|
|
|
|
|
atLeastMean++;
|
|
|
|
|
//--- The MAX over columns is compared against the null distribution OF THE MAX, which corrects for
|
|
|
|
|
//--- testing 26 features at once by construction - no Bonferroni needed, and far less conservative.
|
|
|
|
|
if(m_miBestColumn >= signalBestCol)
|
|
|
|
|
atLeastBestCol++;
|
feat(features): per-column MI keep-screen (report only)
Step 1 of the prune, stopping deliberately short of pruning - two blockers make
an immediate mask the wrong move, and this is the measurement that decides
whether pruning is worth doing at all.
WHY NOT PRUNE YET:
* the screen runs with cross-asset ABSENT - its own log line says the numbers
"describe a NARROWER vector than training will use". A mask built from it
would have no evidence either way about the cross-asset block.
* a per-chart mask FRAGMENTS THE POOL. The mask must participate in the
fingerprint, and the pool only accepts peers with an identical feature
layout. Pooling is currently the only thing keeping the FX trio off the
capacity floor - the three pool-poor charts (SP500, XAUUSD, XTIUSD) are
exactly the three still floored. Six per-chart masks = six pool groups of
one, and pruning could cost more capacity than it buys.
WHAT THIS ADDS: the per-column MI was always computed inside ScoreMiSample and
thrown away except for the sum and the max. It is retained now, and the same
permutation draws that build the headline null also accumulate a PER-COLUMN null,
which is what a per-column p-value needs - distinct from the null-of-the-max,
which answers the single family-wise question "is the strongest column real".
Selection uses Benjamini-Hochberg at q=0.10, NOT the family-wise bar. FWER
controls the chance of one false positive, which is right for a verdict and far
too conservative for selection - it would discard every genuinely weak-but-useful
feature. BH bounds the expected SHARE of kept columns that are noise, which is
what a feature set cares about.
The report prints the decision in capacity units: columns kept, the resulting
input width, and the first-layer budget before and after against the 16-wide
floor. 3 of 52 is not a feature set; 45 of 52 is not worth a fingerprint re-key.
The cross-asset caveat prints itself when it applies.
Context that makes this worth doing at all: under the pivot-event label the MI
screen now reads "above the noise floor - a real association" - mean 4x the null
(p=0.005), strongest column 7.7x the null-max, excess 0.80% of label entropy,
against 1.3x / 1.15x / ~0.1% under the old label. The noise-floor verdict that
closed several earlier directions was a property of the OLD label.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 15:43:59 -04:00
|
|
|
//--- PER-COLUMN null, accumulated from the same draws. Each column gets its OWN null
|
|
|
|
|
//--- distribution here, which is what a per-column p-value needs - distinct from the
|
|
|
|
|
//--- null-of-the-max above, which answers the single family-wise question "is the strongest
|
|
|
|
|
//--- column real" and is far too conservative to select a SET with.
|
|
|
|
|
for(int f = 0; f < colN && f < ArraySize(m_miColumn); f++)
|
|
|
|
|
if(m_miColumn[f] >= obsCol[f])
|
|
|
|
|
colAtLeast[f]++;
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
draws++;
|
|
|
|
|
}
|
|
|
|
|
//--- Left the null early because the program is going away: everything from here on either prints a
|
|
|
|
|
//--- number derived from `draws` or starts another scan. Neither is worth a millisecond of the teardown
|
2026-08-24 21:01:08 -04:00
|
|
|
//--- budget.
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
if(ShutdownRequested())
|
|
|
|
|
return;
|
|
|
|
|
double floorMean = (draws > 0) ? floorSum / draws : -1.0;
|
|
|
|
|
double floorVar = (draws > 1) ? MathMax(0.0, floorSumSq / draws - floorMean * floorMean) : 0.0;
|
|
|
|
|
double floorSd = MathSqrt(floorVar * (draws > 1 ? (double)draws / (draws - 1) : 1.0));
|
|
|
|
|
double floorBestCol = (draws > 0) ? floorBestColSum / draws : -1.0;
|
2026-08-24 04:08:47 -04:00
|
|
|
double pMean = PermutationPValue(atLeastMean, draws);
|
|
|
|
|
double pBestCol = PermutationPValue(atLeastBestCol, draws);
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
//--- Two SEPARATE questions, because at these sample sizes a small p can accompany a worthless
|
|
|
|
|
//--- effect. (1) Is it real - the p-values. Both are printed; neither is collapsed into a verdict
|
|
|
|
|
//--- that hides the other.
|
|
|
|
|
double excessShare = (labelEntropy > 1e-9 && floorMean >= 0.0)
|
|
|
|
|
? 100.0 * (observed - floorMean) / labelEntropy : 0.0;
|
|
|
|
|
string verdict = (draws > 0 && pMean <= 0.05)
|
|
|
|
|
? "above the noise floor - a real association"
|
|
|
|
|
: "AT THE NOISE FLOOR - indistinguishable from shuffled labels";
|
|
|
|
|
//--- Name the feature vector this was measured on. Stating the width and the panel's presence makes
|
|
|
|
|
//--- that mismatch visible in the log instead of requiring a timestamp comparison.
|
|
|
|
|
string vecNote = StringFormat("%d features/bar, cross-asset %s", m_neuronsCount,
|
|
|
|
|
m_crossAsset.IsReady()
|
|
|
|
|
? "PRESENT"
|
|
|
|
|
: "ABSENT (reference symbols unsynchronised - these numbers describe "
|
|
|
|
|
"a NARROWER vector than training will use)");
|
|
|
|
|
Print(ID + StringFormat(": feature/label information - %.5f nats/feature vs a shuffled-label null of "
|
|
|
|
|
"%.5f +/- %.5f over %d permutations, p=%.4f; strongest single feature %.5f vs "
|
|
|
|
|
"%.5f (null max, p=%.4f); excess is %.2f%% of the label's %.3f nats of entropy "
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
"(%d samples %d bars apart = %d independent blocks over a %.1f-bar mean label "
|
|
|
|
|
"resolution, %.1fs) [%s]. %s.",
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
observed, floorMean, floorSd, draws, pMean,
|
|
|
|
|
signalBestCol, floorBestCol, pBestCol, excessShare, labelEntropy,
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
nSample, m_miStrideBars, m_miNullBlocks, MeanLabelLifespan(),
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
(GetTickCount() - tPerm) / 1000.0, vecNote, verdict));
|
feat(features): per-column MI keep-screen (report only)
Step 1 of the prune, stopping deliberately short of pruning - two blockers make
an immediate mask the wrong move, and this is the measurement that decides
whether pruning is worth doing at all.
WHY NOT PRUNE YET:
* the screen runs with cross-asset ABSENT - its own log line says the numbers
"describe a NARROWER vector than training will use". A mask built from it
would have no evidence either way about the cross-asset block.
* a per-chart mask FRAGMENTS THE POOL. The mask must participate in the
fingerprint, and the pool only accepts peers with an identical feature
layout. Pooling is currently the only thing keeping the FX trio off the
capacity floor - the three pool-poor charts (SP500, XAUUSD, XTIUSD) are
exactly the three still floored. Six per-chart masks = six pool groups of
one, and pruning could cost more capacity than it buys.
WHAT THIS ADDS: the per-column MI was always computed inside ScoreMiSample and
thrown away except for the sum and the max. It is retained now, and the same
permutation draws that build the headline null also accumulate a PER-COLUMN null,
which is what a per-column p-value needs - distinct from the null-of-the-max,
which answers the single family-wise question "is the strongest column real".
Selection uses Benjamini-Hochberg at q=0.10, NOT the family-wise bar. FWER
controls the chance of one false positive, which is right for a verdict and far
too conservative for selection - it would discard every genuinely weak-but-useful
feature. BH bounds the expected SHARE of kept columns that are noise, which is
what a feature set cares about.
The report prints the decision in capacity units: columns kept, the resulting
input width, and the first-layer budget before and after against the 16-wide
floor. 3 of 52 is not a feature set; 45 of 52 is not worth a fingerprint re-key.
The cross-asset caveat prints itself when it applies.
Context that makes this worth doing at all: under the pivot-event label the MI
screen now reads "above the noise floor - a real association" - mean 4x the null
(p=0.005), strongest column 7.7x the null-max, excess 0.80% of label entropy,
against 1.3x / 1.15x / ~0.1% under the old label. The noise-floor verdict that
closed several earlier directions was a property of the OLD label.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 15:43:59 -04:00
|
|
|
//--- WHICH columns carry it, not just whether one does. Report-only: nothing prunes on this yet, and
|
|
|
|
|
//--- the number it produces is exactly what decides whether pruning is worth doing - a cut that keeps
|
|
|
|
|
//--- 3 of 52 columns is not a feature set, and one that keeps 45 is not worth a fingerprint re-key.
|
|
|
|
|
if(draws > 0 && colN > 0)
|
|
|
|
|
{
|
|
|
|
|
double pcol[];
|
|
|
|
|
ArrayResize(pcol, colN);
|
|
|
|
|
for(int f = 0; f < colN; f++)
|
|
|
|
|
pcol[f] = PermutationPValue(colAtLeast[f], draws);
|
|
|
|
|
double sorted[];
|
|
|
|
|
ArrayResize(sorted, colN);
|
|
|
|
|
ArrayCopy(sorted, pcol);
|
|
|
|
|
ArraySort(sorted);
|
|
|
|
|
//--- Benjamini-Hochberg: the largest k with p_(k) <= (k/m)*q; keep every column at or below it.
|
|
|
|
|
int keep = 0;
|
|
|
|
|
for(int k = 1; k <= colN; k++)
|
|
|
|
|
if(sorted[k - 1] <= (double)k / colN * MI_KEEP_FDR_Q)
|
|
|
|
|
keep = k;
|
|
|
|
|
double cutP = (keep > 0) ? sorted[keep - 1] : 0.0;
|
2026-08-26 16:09:47 -04:00
|
|
|
//--- THE KEPT SET AS A HEX BITMASK, column 0 = bit 0 of the first nibble-group. Emitted so the
|
|
|
|
|
//--- masks of two charts can be compared by eye and by grep. That comparison is the open
|
|
|
|
|
//--- question a mask cannot be built without: identical masks across the fleet mean ONE
|
|
|
|
|
//--- fleet-wide mask keeps every chart in a single pool group, while divergent masks would
|
|
|
|
|
//--- split six charts into six groups of one - and pooling is the only thing currently holding
|
|
|
|
|
//--- the FX charts above the capacity floor, so a prune could cost more than it buys.
|
|
|
|
|
string maskHex = "";
|
|
|
|
|
for(int b = 0; b < colN; b += 4)
|
|
|
|
|
{
|
|
|
|
|
int nib = 0;
|
|
|
|
|
for(int q = 0; q < 4 && b + q < colN; q++)
|
|
|
|
|
if(pcol[b + q] <= cutP && keep > 0)
|
|
|
|
|
nib |= (1 << q);
|
|
|
|
|
maskHex += StringFormat("%X", nib);
|
|
|
|
|
}
|
feat(features): per-column MI keep-screen (report only)
Step 1 of the prune, stopping deliberately short of pruning - two blockers make
an immediate mask the wrong move, and this is the measurement that decides
whether pruning is worth doing at all.
WHY NOT PRUNE YET:
* the screen runs with cross-asset ABSENT - its own log line says the numbers
"describe a NARROWER vector than training will use". A mask built from it
would have no evidence either way about the cross-asset block.
* a per-chart mask FRAGMENTS THE POOL. The mask must participate in the
fingerprint, and the pool only accepts peers with an identical feature
layout. Pooling is currently the only thing keeping the FX trio off the
capacity floor - the three pool-poor charts (SP500, XAUUSD, XTIUSD) are
exactly the three still floored. Six per-chart masks = six pool groups of
one, and pruning could cost more capacity than it buys.
WHAT THIS ADDS: the per-column MI was always computed inside ScoreMiSample and
thrown away except for the sum and the max. It is retained now, and the same
permutation draws that build the headline null also accumulate a PER-COLUMN null,
which is what a per-column p-value needs - distinct from the null-of-the-max,
which answers the single family-wise question "is the strongest column real".
Selection uses Benjamini-Hochberg at q=0.10, NOT the family-wise bar. FWER
controls the chance of one false positive, which is right for a verdict and far
too conservative for selection - it would discard every genuinely weak-but-useful
feature. BH bounds the expected SHARE of kept columns that are noise, which is
what a feature set cares about.
The report prints the decision in capacity units: columns kept, the resulting
input width, and the first-layer budget before and after against the 16-wide
floor. 3 of 52 is not a feature set; 45 of 52 is not worth a fingerprint re-key.
The cross-asset caveat prints itself when it applies.
Context that makes this worth doing at all: under the pivot-event label the MI
screen now reads "above the noise floor - a real association" - mean 4x the null
(p=0.005), strongest column 7.7x the null-max, excess 0.80% of label entropy,
against 1.3x / 1.15x / ~0.1% under the old label. The noise-floor verdict that
closed several earlier directions was a property of the OLD label.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 15:43:59 -04:00
|
|
|
//--- The whole point of the exercise, stated in the units the capacity budget uses.
|
|
|
|
|
int widthNow = m_historyBars * colN;
|
|
|
|
|
int widthKept = m_historyBars * MathMax(keep, 1);
|
|
|
|
|
double effN = EffectiveSampleSize((double)EstimatedInSampleBarsRaw());
|
|
|
|
|
Print(ID + StringFormat(": feature keep-screen (REPORT ONLY, nothing prunes yet) - %d of %d"
|
|
|
|
|
" columns clear Benjamini-Hochberg at q=%.2f (cut p<=%.4f). Input width"
|
|
|
|
|
" would go %d -> %d (%d bars x columns), and the first-layer budget"
|
|
|
|
|
" effN/(width+1) %.1f -> %.1f against a %d-wide floor.%s",
|
|
|
|
|
keep, colN, MI_KEEP_FDR_Q, cutP, widthNow, widthKept, m_historyBars,
|
|
|
|
|
effN / (widthNow + 1.0), effN / (widthKept + 1.0), FIRST_LAYER_MIN_WIDTH,
|
|
|
|
|
(m_crossAsset.IsReady() ? ""
|
|
|
|
|
: " CAVEAT: measured with cross-asset ABSENT, so these columns are a"
|
|
|
|
|
" SUBSET of what training uses - a mask built from this would not"
|
|
|
|
|
" cover the cross-asset block at all.")));
|
2026-08-26 16:09:47 -04:00
|
|
|
Print(ID + ": feature keep-mask " + maskHex + " (hex, column 0 = low bit; compare across charts"
|
|
|
|
|
" before building a real mask - divergent masks would fragment the training pool).");
|
feat(features): per-column MI keep-screen (report only)
Step 1 of the prune, stopping deliberately short of pruning - two blockers make
an immediate mask the wrong move, and this is the measurement that decides
whether pruning is worth doing at all.
WHY NOT PRUNE YET:
* the screen runs with cross-asset ABSENT - its own log line says the numbers
"describe a NARROWER vector than training will use". A mask built from it
would have no evidence either way about the cross-asset block.
* a per-chart mask FRAGMENTS THE POOL. The mask must participate in the
fingerprint, and the pool only accepts peers with an identical feature
layout. Pooling is currently the only thing keeping the FX trio off the
capacity floor - the three pool-poor charts (SP500, XAUUSD, XTIUSD) are
exactly the three still floored. Six per-chart masks = six pool groups of
one, and pruning could cost more capacity than it buys.
WHAT THIS ADDS: the per-column MI was always computed inside ScoreMiSample and
thrown away except for the sum and the max. It is retained now, and the same
permutation draws that build the headline null also accumulate a PER-COLUMN null,
which is what a per-column p-value needs - distinct from the null-of-the-max,
which answers the single family-wise question "is the strongest column real".
Selection uses Benjamini-Hochberg at q=0.10, NOT the family-wise bar. FWER
controls the chance of one false positive, which is right for a verdict and far
too conservative for selection - it would discard every genuinely weak-but-useful
feature. BH bounds the expected SHARE of kept columns that are noise, which is
what a feature set cares about.
The report prints the decision in capacity units: columns kept, the resulting
input width, and the first-layer budget before and after against the 16-wide
floor. 3 of 52 is not a feature set; 45 of 52 is not worth a fingerprint re-key.
The cross-asset caveat prints itself when it applies.
Context that makes this worth doing at all: under the pivot-event label the MI
screen now reads "above the noise floor - a real association" - mean 4x the null
(p=0.005), strongest column 7.7x the null-max, excess 0.80% of label entropy,
against 1.3x / 1.15x / ~0.1% under the old label. The noise-floor verdict that
closed several earlier directions was a property of the OLD label.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 15:43:59 -04:00
|
|
|
}
|
fix(topology): stop a training-alone size becoming permanent, and stop the keep-screen latching underpowered
1. THE POOL FIX WAS LANDING ON A TOPOLOGY THAT COULD NOT SEE IT.
ComputeFirstLayerWidth budgets against EstimatedInSampleBars, which counts
this chart's own bars PLUS the training pool. On a COLD fleet start every
chart derives and pins its topology BEFORE any chart has published a pool
file - measured on the 18:13 start, model creation at 18:13:21 against a
first publish at 18:13:48. All six sized as if training alone, wrote that
into .cfg, and adopted it back on every later start even with the pool full.
SP500 ran a first layer floored to 16 while adopting 30229 peer rows.
Adopt-don't-compare exists to protect weights shaped by those sizes. It was
also running for a model with NO .nnw, where there is nothing to protect and
the .cfg is just a record of one unlucky moment. The four derived sizes are
now re-measured when no weights exist.
Safe on all three counts that matter: free (nothing to discard), cannot loop
(once weights exist the .cfg is authoritative again), and cannot fragment the
pool - the derived width is NOT in BuildModelFingerprint, which keys only on
the FEATURE layout. Verified: field 2 of the fingerprint is
LEGACY_HISTORY_BARS_SLOT, not the first-layer width.
TO TAKE EFFECT the weights must be wiped while the TrainPool is KEPT - the
census has to be non-empty at derivation time. A full wipe empties the pool
and reproduces the original condition exactly.
2. THE KEEP-SCREEN LATCHED ON AN UNDERPOWERED SAMPLE.
MI_MIN_SAMPLES is a floor for "can this be computed", and it was being used
as the bar for "is this answer final". The screen fired on the first era
clearing 200 rows and latched, measuring at 202-773 samples where a warm
chart gives ~2065. Columns kept then tracked SAMPLE SIZE rather than
information - EURUSD kept 0 of 49 at n=202, SP500 kept 15 at n=773, and the
ordering across all six charts was very nearly monotone in n.
A thin sample is still measured and printed, but it no longer closes the
question: below MI_GOOD_SAMPLE_FRACTION of the target the result is labelled
underpowered and a later era supersedes it, bounded by the same attempt
budget. An underpowered screen that latches is worse than one that waits,
because it looks like a result.
Build tag -> fleet-pool-v2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 19:27:48 -04:00
|
|
|
//--- LATCH ONLY ON A SAMPLE WORTH KEEPING. A thin sample still gets measured and printed - the
|
|
|
|
|
//--- numbers are real for what they are - but it does not close the question, so a later era with
|
|
|
|
|
//--- more resolved labels can supersede it. Bounded by the same attempt budget as the deferral path,
|
|
|
|
|
//--- so a chart that never reaches the target still ends up with its best available answer.
|
|
|
|
|
int goodSample = (int)(MI_SAMPLE_BARS * MI_GOOD_SAMPLE_FRACTION);
|
|
|
|
|
if(nSample < goodSample && m_miReportAttempts < MI_REPORT_MAX_ATTEMPTS - 1)
|
|
|
|
|
{
|
|
|
|
|
m_miReportAttempts++;
|
|
|
|
|
Print(ID + StringFormat(": that screen is UNDERPOWERED and does NOT close the question - %d"
|
|
|
|
|
" samples against a %d target (%d of %d cached bars carry a resolved"
|
|
|
|
|
" label). Columns kept at this sample size track POWER as much as"
|
|
|
|
|
" information. Re-measuring on a later era (attempt %d of %d).",
|
|
|
|
|
nSample, goodSample, LabelCacheResolvedCount(), m_labelCacheBars,
|
|
|
|
|
m_miReportAttempts, MI_REPORT_MAX_ATTEMPTS));
|
|
|
|
|
return;
|
|
|
|
|
}
|
feat(pool,mi): one feature layout fleet-wide, and the keep-screen stops self-disabling on a cold start
TWO CHANGES, BOTH RETRAIN-FORCING BY INTENT.
1. SP500 was training alone, and one alt-data column was the reason.
The alt block's width joins the model fingerprint, and the pool reader only
adopts peer rows whose fingerprint and width match. The exporter gives each
instrument the series that apply to it - FX 15 columns, metals/oil 14, SP500
13 - so the fleet ran as three incompatible pools:
EURUSD/USDJPY/USDCAD adopt ~57-60k peer rows each
XAUUSD/XTIUSD adopt 6.4k / 20.3k
SP500 "EVERY peer file was REJECTED, so this chart is
training alone" - 0 rows
SP500 therefore trained on 2279 independent observations against a 600-wide
input with its first layer floored at 16, printing its own "expect
overfitting" warning. It is the one chart with no pool and the worst
capacity ratio in the fleet by a factor of three.
Fresh models now pin ALTDATA_FLEET_COLUMNS - the 12-column intersection -
instead of their own file header. An existing model still adopts its .cfg
pin, so this re-keys nothing that is already trained.
Intersection rather than union: filling an absent series with its median
makes that column constant per instrument, which lets a pooled model
identify the source instrument and stop learning the shared mechanism. It
is also 6 columns narrower. Cost is six columns whose retained information
is UNMEASURED - the keep-screen reports a bitmask nothing has mapped back
to names.
2. The MI keep-screen disabled itself for the whole run on any cold start.
ReportFeatureLabelInformation set m_miReportDone on ENTRY. On a cold start
the label cache is allocated before it is filled, so BuildMiSample finds no
row carrying a resolved label and returns 0 - a sixth exit, and the only
one the 8c1266d instrumentation did not cover, which is why it printed
nothing. observed then stayed -1, the permutation loop never iterated, and
the report emitted "-1.00000 nats over 0 permutations" beside a plausible
"strongest single feature 0.05979" that was a STALE m_miBestColumn from an
earlier scoring call. The first ensemble member propagated the latch to
g_ensembleChartMiReportDone and silenced every member on the chart.
The flag now latches only once a measurement exists. A short sample is
reported as a deferral naming the two numbers that identify it (cached bars
vs bars carrying a resolved label) and retried, up to
MI_REPORT_MAX_ATTEMPTS.
Build tag -> fleet-pool-v1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:41:14 -04:00
|
|
|
//--- THE MEASUREMENT EXISTS NOW, so latch. Everything below is commentary and a positive control,
|
|
|
|
|
//--- each with its own teardown escape - none of it changes whether this era measured the screen.
|
|
|
|
|
m_miReportDone = true;
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
//--- POWER, stated up front. Saying so prevents the opposite error to the one this replaced -
|
|
|
|
|
//--- reading "not significant" as "no signal" when it means "not enough independent data to tell".
|
|
|
|
|
if(m_miNullBlocks > 0 && m_miNullBlocks < 30)
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
Print(ID + StringFormat(": NOTE - only %d independent label blocks in this sample (%.1f-bar mean "
|
|
|
|
|
"label resolution, %d-bar sampling stride). The rows overlap heavily, so "
|
|
|
|
|
"this test has little power: treat a non-significant result here as 'not "
|
|
|
|
|
"enough independent history to answer', not as 'no signal'. More history "
|
|
|
|
|
"is what would settle it.", m_miNullBlocks, MeanLabelLifespan(),
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
m_miStrideBars));
|
|
|
|
|
//--- Stated every time, not only on a bad result: this measure is MARGINAL and PER-BAR, while the network
|
|
|
|
|
//--- reads m_historyBars bars at once. It can therefore only ever prove that signal EXISTS, never that it
|
|
|
|
|
//--- does not - an interaction across features or across time is invisible to it by construction. Said
|
|
|
|
|
//--- out loud so a floor-level reading is not over-read into "this instrument is unpredictable".
|
|
|
|
|
if(!(draws > 0 && pMean <= 0.05))
|
|
|
|
|
Print(ID + ": NOTE - that measure is marginal (one feature at a time) and per-bar, whereas the "
|
|
|
|
|
"network sees " + IntegerToString((int)m_historyBars) + " bars jointly. A floor-level reading "
|
|
|
|
|
"rules out a simple per-feature edge; it cannot rule out one that only exists in combination "
|
|
|
|
|
"or across time. It does mean no per-feature indicator retuning will help.");
|
|
|
|
|
if(observed < 0.0)
|
|
|
|
|
return;
|
|
|
|
|
//--- POSITIVE CONTROL. A floor reading is therefore worthless until the instrument is shown to
|
|
|
|
|
//--- respond to a signal that is KNOWN to be there. This one is free: the label of a NEIGHBOURING
|
|
|
|
|
//--- sample row.
|
|
|
|
|
if(ShutdownRequested())
|
|
|
|
|
return;
|
|
|
|
|
double controlMi = -1.0, decorrMi = -1.0;
|
|
|
|
|
int controlBars = 1;
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
int decorrBars = (int)MathMax(1.0, MeanLabelLifespan() / 4.0);
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
{
|
|
|
|
|
//--- Rebuilt rather than reused because the permutation loop above destroyed the honest label
|
|
|
|
|
//--- ordering, and controlling against a shuffled array would measure the floor twice.
|
|
|
|
|
double c0[], cK[];
|
|
|
|
|
int l0[], lK[];
|
|
|
|
|
int n0 = BuildMiSample(c0, l0);
|
|
|
|
|
if(n0 >= MI_MIN_SAMPLES)
|
|
|
|
|
{
|
|
|
|
|
int offs[2];
|
|
|
|
|
offs[0] = controlBars;
|
|
|
|
|
offs[1] = decorrBars;
|
|
|
|
|
for(int oi = 0; oi < 2; oi++)
|
|
|
|
|
{
|
|
|
|
|
int nK = BuildMiSample(cK, lK, offs[oi]);
|
|
|
|
|
//--- Both builds are padded by the SAME fixed amount, so they enumerate the same bars with
|
|
|
|
|
//--- the same stride and row k of one is row k of the other. Sized from what actually came
|
|
|
|
|
//--- back, never from the caller's count.
|
|
|
|
|
int nc = MathMin(n0, nK);
|
|
|
|
|
if(nc < MI_MIN_SAMPLES)
|
|
|
|
|
continue;
|
|
|
|
|
double neighbourLabel[];
|
|
|
|
|
int selfLabels[];
|
|
|
|
|
ArrayResize(neighbourLabel, nc);
|
|
|
|
|
ArrayResize(selfLabels, nc);
|
|
|
|
|
for(int k = 0; k < nc; k++)
|
|
|
|
|
{
|
|
|
|
|
selfLabels[k] = l0[k];
|
|
|
|
|
neighbourLabel[k] = (double)lK[k];
|
|
|
|
|
}
|
|
|
|
|
double v = FeatureColumnMI(neighbourLabel, selfLabels, nc);
|
|
|
|
|
if(oi == 0)
|
|
|
|
|
controlMi = v;
|
|
|
|
|
else
|
|
|
|
|
decorrMi = v;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
Print(ID + StringFormat(": MI positive control - the ADJACENT bar's label (mean resolution %.1f bars) "
|
|
|
|
|
"scores %.5f nats against the ~%.5f noise floor; by a quarter of that (%d bars) "
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
"it is already down to %.5f, which is how fast this target decorrelates. %s",
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
MeanLabelLifespan(),
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
controlMi, floorMean, decorrBars, decorrMi,
|
|
|
|
|
(controlMi > floorMean * 5.0)
|
|
|
|
|
? "The estimator detects a known association on this exact data, so a "
|
|
|
|
|
"floor-level reading above is a real finding and not a broken measurement."
|
|
|
|
|
: "WARNING - the estimator FAILED to detect an association that must be there. "
|
|
|
|
|
"Every mutual-information figure above is void; fix this before drawing any "
|
|
|
|
|
"conclusion from them."));
|
|
|
|
|
//--- ALIGNMENT SCAN. Both destroy the information before any topology sees it, and both look
|
|
|
|
|
//--- identical in every accuracy number this EA prints - which is exactly why four different
|
|
|
|
|
//--- architectures all landed on the same precision.
|
|
|
|
|
int offsets[] = { -5, -3, -2, -1, 0, 1, 2, 3, 5 };
|
|
|
|
|
string profile = "";
|
|
|
|
|
double atZero = -1.0, worstFuture = -1.0, farPast = -1.0;
|
|
|
|
|
int worstFutureK = 0;
|
|
|
|
|
for(int oi = 0; oi < ArraySize(offsets); oi++)
|
|
|
|
|
{
|
|
|
|
|
//--- Nine sample builds. A partial profile cannot be read - the lookahead test compares the k<0
|
|
|
|
|
//--- side against k=0 and both must exist - so a stop abandons the scan rather than printing a row
|
|
|
|
|
//--- with holes in it that would look like a null result at the missing offsets.
|
|
|
|
|
if(ShutdownRequested())
|
|
|
|
|
return;
|
|
|
|
|
double oc[];
|
|
|
|
|
int ol[];
|
|
|
|
|
int on = BuildMiSample(oc, ol, offsets[oi]);
|
|
|
|
|
double os = (on >= MI_MIN_SAMPLES) ? ScoreMiSample(oc, ol, on, false) : -1.0;
|
|
|
|
|
profile += StringFormat("%s%+d:%.5f", (oi > 0 ? " " : ""), offsets[oi], os);
|
|
|
|
|
if(offsets[oi] == 0)
|
|
|
|
|
atZero = os;
|
|
|
|
|
else
|
|
|
|
|
if(offsets[oi] < 0 && os > worstFuture)
|
|
|
|
|
{
|
|
|
|
|
worstFuture = os;
|
|
|
|
|
worstFutureK = offsets[oi];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if(offsets[oi] > 0)
|
|
|
|
|
farPast = os; // offsets ascend, so this ends on the largest k
|
|
|
|
|
}
|
|
|
|
|
//--- A MARGIN, not a bare comparison. Every one of these offsets is an estimate with the same noise
|
|
|
|
|
//--- as the headline statistic, so "k=-3 came out above k=0" is meaningless when the gap is smaller
|
|
|
|
|
//--- than the null's own spread.
|
|
|
|
|
double lookaheadMargin = 3.0 * floorSd;
|
|
|
|
|
string alignVerdict;
|
|
|
|
|
if(worstFuture > atZero + lookaheadMargin)
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
alignVerdict = StringFormat(" | LOOKAHEAD - k=%d (a label whose swing leg opens AFTER these "
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
"features exist) scores %.5f against %.5f at k=0, clearing the %.5f "
|
|
|
|
|
"margin (3 sd of the null). The features can only score there by "
|
|
|
|
|
"containing future information. Fix that before trusting any accuracy "
|
|
|
|
|
"number this EA prints.", worstFutureK, worstFuture, atZero, lookaheadMargin);
|
|
|
|
|
else
|
|
|
|
|
alignVerdict = StringFormat(" | clean: no future label (k<0) beats k=0, so there is no lookahead. "
|
feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00
|
|
|
"The rise on the k>0 side is expected - those legs are already under "
|
|
|
|
|
"way, so the features hold part of the answer - and its size is the "
|
|
|
|
|
"finding: %.5f at k=+5 against %.5f at k=0, i.e. ~%.1fx more is "
|
|
|
|
|
"knowable 5 bars into a leg than at the entry the model actually trades.",
|
|
|
|
|
farPast, atZero, (atZero > 1e-9 ? farPast / atZero : 0.0));
|
refactor(mi): split AutoTune.mqh - SEARCH vs MEASUREMENT
Session C of the feature-selection/labeling refactor track. AutoTune.mqh was
two responsibilities in one 1,584-line file: SEARCH (TuneIndicatorsByFilter,
coordinate-descent over indicator settings) and MEASUREMENT (the "does this
feature vector predict this label at all" evidence screen and its three
sub-reports). Split along that seam into a new Expert/AIBase/FeatureScreen.mqh.
Moved, verbatim (diffed byte-for-byte against the pre-split content - zero
lines differ beyond the file-boundary comment headers): ReportFeatureLabel-
Information, ReportExcursionInformation, ReportFeatureLagProfile, Report-
BarrierGeometryScan, ApplyAdoptedGeometry.
Stayed in AutoTune.mqh: the MI engine (FeatureColumnMI/BuildMiSample/
ScoreMiSample) both files call - a shared dependency used by two consumers is
not itself a reason to split further; TuneIndicatorsByFilter; the export
utilities; and TuneIndicatorsAndTrain, the entry point that decides which of
the two branches a given model runs - it is the coordinator, not a member of
either side.
Still body-only method definitions of CExpertSignalAIBase, same as every
other Expert\AIBase\*.mqh file - MQL5 has no partial classes, so this is a
file-organisation move (legibility, SRP-per-file), not a coupling reduction.
The include site says order between AIBase\*.mqh files is irrelevant, so the
new include was added next to AutoTune.mqh's; the file-scope g_ensembleChart*
globals both files reference stay declared in AutoTune.mqh's header, ahead of
the new include either way.
Verified: brace counts split exactly 105 -> 57+48; every one of the 14
function definitions HEAD had in AutoTune.mqh accounted for in exactly one of
the two files, no duplicates; the WARRIOR_EXPORT_FEATURES ifdef/endif pair
(unrelated, lines 35/147) untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:08:36 -04:00
|
|
|
Print(ID + ": MI label-alignment scan (label from bar i+k; higher index = OLDER bar, so k<0 is the "
|
|
|
|
|
"future) - " + profile + alignVerdict);
|
|
|
|
|
}
|
|
|
|
|
#endif // WARRIOR_AIBASE_FEATURESCREEN_MQH
|