Resurrecting the diagnostic in 4113afd immediately showed why it needed more
than resurrecting. Its first output, on every chart:
strongest of 49 columns x 7999 lags is column 4 at lag +3900, |r| 0.5120
(32.4 SE of the 0.0158 no-information band) -> SURVIVES
XTIUSD and SP500 named the SAME column at the SAME lag with |r| within 0.01 of
each other. Two independent instruments cannot agree to that precision at a
3900-bar lag; that is what identified it as an artifact rather than a finding.
TWO DEFECTS, both of which manufacture significance at long lags.
1. THE NO-INFORMATION BAND WAS GLOBAL, THE CORRELATION IS NOT.
CorrR1D is non-circular: lag k is computed from (n - |k|) overlapping terms
while the normaliser uses all n. Scoring every lag against one 1/sqrt(n)
band understates it by sqrt(n/(n-|k|)) - a factor of 6 at the edge. Lag
+3900 of 3999 rests on ~100 overlapping terms and was being judged as if it
rested on 4000. Each lag now gets its own band, deflated for label
persistence, and the maximum is ranked on z rather than |r| - two equal
correlations are not equally surprising on different sample sizes.
2. THE LAG RANGE ANSWERED NO QUESTION.
The profile exists to say how deep a lookback carries linear structure, and
the input window is HistoryBars. Lags two orders of magnitude past it are
both meaningless and where this estimator is worst. Capped at 4x the window.
The Sidak family is now the lags actually searched, not 2n-1.
Left deliberately: this remains a LINEAR, marginal measure. It can bound the
useful lookback; it cannot prove a shorter window loses nothing.
Build tag -> lagprofile-v2. Not a layout change: no retrain.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every chart, every era, on every run in the logs: "linear lag profile skipped -
only 0 contiguous OOS bars." That reads as "not enough data". It was not. The
walk never started.
ReportLinearLagProfile walks newest-first from r=0 and breaks on the first row
without a label, to avoid splicing across a hole. But r=0 IS the newest bar,
and a forward-looking swing-pivot label cannot be resolved there by
construction - the opposite pivot has not committed yet. So HasLabel(0) is
false, the loop breaks on its first iteration, and n=0. Permanently.
The leading gap is SYSTEMATIC (always about the label resolution), not a hole
in the middle of the series, so stepping over it splices nothing. Contiguity is
still enforced from the first labelled row onward.
WHY THIS MATTERS BEYOND THE DIAGNOSTIC: the input window is 12 bars, and the
capacity budget divides by width = columns x bars. Cutting the window is the
largest lever left for the three charts still pinned to the 16-unit first-layer
floor, and there has been no measurement of whether the deeper lags carry
anything - because the one diagnostic that would answer it has never produced a
number. The old lag verdict in memory predates the pivot-event label.
The skip message now reports where the walk ran out, so "0 from r=0" (never
started) is distinguishable from "0 from r=37" (genuinely short window).
Build tag -> lagprofile-v1. NOT a feature-layout change: no retrain, models
resume from their weights and the training pool stays valid.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Baselines was 951 lines of CExpertSignalAIBase method bodies in a file
that only looked like a module. It is now CBaselineComparator: a class
the signal OWNS, which reads a CTrainingDataView and prints. It does
not name the signal anywhere in its code.
What the seam forced out into the open:
- Thirty-odd ArraySize() bounds tests, each carried by its caller, are
now one test per accessor next to the data. The two `hasValueN` and
one `arrowN` locals are gone with them.
- The -2.0 "never scored" sentinel on the arrow cache was tested at the
call site. It is now inside DataDirectionalCall, where it cannot be
read as a small confidence.
- DoubleToSignal needs m_outputNeuronsCount, so a raw double could not
be turned into a side by any reader. The view answers
DirectionalCall(bar, isBuy, magnitude) instead - the conversion
happens where the head width lives, and the module no longer needs
ENUM_SIGNAL at all.
- m_baselineDone was a latch on the signal for a decision only this
module makes. It is m_done, private, where it belongs.
Correction to my own earlier claim: I said Baselines had nine exclusive
members "polluting the signal class". It had none. m_x, m_f, m_ngrad,
m_AvgCE and the rest are FIELDS OF ALGLIB REPORT OBJECTS (state.m_x,
mrep.m_AvgCE) that my `\bm_\w+` scan matched after the dot. The module
needs no private state but its view pointer and that latch - which is
why it came out this cleanly.
The include sits below the g_ens* vote globals and the Alglib headers
it reads, because unlike the AIBase\*.mqh partials this is a real class
declaration compiled where it stands.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 12:00:18 -04:00
Renamed from Expert/AIBase/Baselines.mqh (Browse further)