refactor(arch): a read-only training-data view, so modules stop being #included code
The AIBase\*.mqh files are not modules. They are method bodies of one
3,400-line class, textually #included after its declaration. Every one
of them can touch every member of every other, which is why "move this
out" has so far meant "move the whole class".
Introduce the seam that ends that:
CTrainingDataView abstract - the ONLY thing a training-side
collaborator may see: a feature row, a label, an
outcome, an excursion, the shape they share, and
the identity to log under.
CAIBaseTrainingData the adapter. MQL5 gives a class exactly one base
and CExpertSignalAIBase is already a
CExpertSignalCustom, so it cannot implement the
view itself. It owns one of these instead.
Data*() on the the published read API the adapter forwards to.
signal MQL5 has no `friend`, so reaching in from outside
was never an option - and making it explicit is
the point rather than a workaround.
Every row accessor OWNS ITS BOUNDS TEST and answers false for a bar it
has nothing for. Thirty-odd call sites currently carry their own
ArraySize() guard; one that forgets reads past a cache that is shorter
than the bar count for the whole warm-up. The -2.0 "never scored"
sentinel on the arrow cache is folded in the same way, so it can no
longer be mistaken for a small confidence.
Nothing uses it yet - this is the seam only, kept as its own commit so
the pattern compiles before 951 lines of Baselines move onto it. The
pattern is the stdlib's own: abstract base with =0 (Canvas\DX\DXObject),
concrete override, forward-declared owner pointer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 11:53:00 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Warrior_EA |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| |
|
|
|
|
|
//| A READ-ONLY VIEW of one model's training data. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#ifndef WARRIOR_TRAINING_ITRAININGDATA_MQH
|
|
|
|
|
#define WARRIOR_TRAINING_ITRAININGDATA_MQH
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| WHAT A TRAINING-SIDE COLLABORATOR IS ALLOWED TO SEE. |
|
|
|
|
|
//| |
|
|
|
|
|
//| Everything that analyses a model's data - baselines, geometry |
|
|
|
|
|
//| scans, redundancy reports - needs the same handful of things: a |
|
|
|
|
|
//| feature row, a label, an outcome, an excursion, and the shape |
|
|
|
|
|
//| they all share. Before this, each one reached straight into |
|
|
|
|
|
//| CExpertSignalAIBase's members, which is why a 951-line diagnostic |
|
|
|
|
|
//| could not be moved, tested or replaced without moving the signal |
|
|
|
|
|
//| class with it. |
|
|
|
|
|
//| |
|
|
|
|
|
//| Every accessor is BOUNDS-CHECKED and returns false rather than |
|
|
|
|
|
//| reading past a cache. The caller asks "is there a label at r?" |
|
|
|
|
|
//| and never "how long is the label array?", so a short cache is a |
|
|
|
|
|
//| missing row here instead of an out-of-range read at each of the |
|
|
|
|
|
//| thirty-odd call sites that used to do the test themselves. |
|
|
|
|
|
//| |
|
|
|
|
|
//| MQL5 has interfaces, but a class may implement one only if it |
|
|
|
|
|
//| inherits nothing else, and CExpertSignalAIBase is already a |
|
|
|
|
|
//| CExpertSignalCustom. So this is an abstract class and the signal |
|
|
|
|
|
//| exposes itself through a small ADAPTER that does inherit it - see |
|
|
|
|
|
//| CAIBaseTrainingData. Collaborators depend on this and never on |
|
|
|
|
|
//| the signal. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CTrainingDataView
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
~CTrainingDataView(void) { }
|
|
|
|
|
//--- SHAPE: the geometry every row shares.
|
|
|
|
|
virtual int HistoryBars(void) = 0; // bars per feature window
|
|
|
|
|
virtual int FeaturesPerBar(void) = 0; // columns per bar
|
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
|
|
|
virtual int LabelResolutionBars(void) = 0; // mean label resolution lag; also the declustering gap
|
refactor(arch): a read-only training-data view, so modules stop being #included code
The AIBase\*.mqh files are not modules. They are method bodies of one
3,400-line class, textually #included after its declaration. Every one
of them can touch every member of every other, which is why "move this
out" has so far meant "move the whole class".
Introduce the seam that ends that:
CTrainingDataView abstract - the ONLY thing a training-side
collaborator may see: a feature row, a label, an
outcome, an excursion, the shape they share, and
the identity to log under.
CAIBaseTrainingData the adapter. MQL5 gives a class exactly one base
and CExpertSignalAIBase is already a
CExpertSignalCustom, so it cannot implement the
view itself. It owns one of these instead.
Data*() on the the published read API the adapter forwards to.
signal MQL5 has no `friend`, so reaching in from outside
was never an option - and making it explicit is
the point rather than a workaround.
Every row accessor OWNS ITS BOUNDS TEST and answers false for a bar it
has nothing for. Thirty-odd call sites currently carry their own
ArraySize() guard; one that forgets reads past a cache that is shorter
than the bar count for the whole warm-up. The -2.0 "never scored"
sentinel on the arrow cache is folded in the same way, so it can no
longer be mistaken for a small confidence.
Nothing uses it yet - this is the seam only, kept as its own commit so
the pattern compiles before 951 lines of Baselines move onto it. The
pattern is the stdlib's own: abstract base with =0 (Canvas\DX\DXObject),
concrete override, forward-declared owner pointer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 11:53:00 -04:00
|
|
|
virtual int PurgeBars(void) = 0; // purge width between fitted and held-out spans
|
|
|
|
|
virtual int CalibrationHiIndex(const int totalIter, const int oosCutoff) = 0;
|
|
|
|
|
//--- ROWS: false means "this bar has nothing to say", never a partial answer.
|
|
|
|
|
virtual bool HasLabel(const int bar) = 0;
|
|
|
|
|
virtual bool IsBuyLabel(const int bar) = 0;
|
|
|
|
|
virtual bool IsSellLabel(const int bar) = 0;
|
|
|
|
|
virtual bool RowFeatures(const int bar, const int width, double &x[]) = 0;
|
refactor(baselines): the first real module - a class, not an #included partial
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
|
|
|
//--- THE MODEL'S OWN CALL on a bar, as the chart drew it: which way and how strongly.
|
|
|
|
|
//--- False = it said nothing (never scored, or scored Neutral). Direction rather than a raw
|
|
|
|
|
//--- double on purpose - turning one into the other needs the head's output width, which is the
|
|
|
|
|
//--- model's business and not a reader's.
|
|
|
|
|
virtual bool DirectionalCall(const int bar, bool &isBuy, double &magnitude) = 0;
|
refactor(arch): a read-only training-data view, so modules stop being #included code
The AIBase\*.mqh files are not modules. They are method bodies of one
3,400-line class, textually #included after its declaration. Every one
of them can touch every member of every other, which is why "move this
out" has so far meant "move the whole class".
Introduce the seam that ends that:
CTrainingDataView abstract - the ONLY thing a training-side
collaborator may see: a feature row, a label, an
outcome, an excursion, the shape they share, and
the identity to log under.
CAIBaseTrainingData the adapter. MQL5 gives a class exactly one base
and CExpertSignalAIBase is already a
CExpertSignalCustom, so it cannot implement the
view itself. It owns one of these instead.
Data*() on the the published read API the adapter forwards to.
signal MQL5 has no `friend`, so reaching in from outside
was never an option - and making it explicit is
the point rather than a workaround.
Every row accessor OWNS ITS BOUNDS TEST and answers false for a bar it
has nothing for. Thirty-odd call sites currently carry their own
ArraySize() guard; one that forgets reads past a cache that is shorter
than the bar count for the whole warm-up. The -2.0 "never scored"
sentinel on the arrow cache is folded in the same way, so it can no
longer be mistaken for a small confidence.
Nothing uses it yet - this is the seam only, kept as its own commit so
the pattern compiles before 951 lines of Baselines move onto it. The
pattern is the stdlib's own: abstract base with =0 (Canvas\DX\DXObject),
concrete override, forward-declared owner pointer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 11:53:00 -04:00
|
|
|
//--- IDENTITY, for log lines and for the once-per-chart guards.
|
|
|
|
|
virtual string Id(void) = 0;
|
|
|
|
|
virtual bool IsEnsembleMember(void) = 0;
|
|
|
|
|
virtual int EnsembleIndex(void) = 0;
|
|
|
|
|
//--- The net's own gate on this chart, for the row every baseline is read against. False when
|
|
|
|
|
//--- the gate has not scored yet, which is NOT the same as a gate that scored zero.
|
|
|
|
|
virtual bool GateReference(double &precPct, int &calls, double &chancePct) = 0;
|
|
|
|
|
//--- POLICY the collaborator must not re-derive: overlapping labels are worth less than their
|
|
|
|
|
//--- count, and only the model knows its own overlap - see EffectiveSampleSize.
|
|
|
|
|
virtual double EffectiveSampleSize(const double rawN) = 0;
|
|
|
|
|
virtual bool Stopping(void) = 0;
|
|
|
|
|
};
|
|
|
|
|
#endif // WARRIOR_TRAINING_ITRAININGDATA_MQH
|
|
|
|
|
//+------------------------------------------------------------------+
|