Warrior_EA/Expert/Training/ITrainingData.mqh

66 lines
4.3 KiB
MQL5
Raw Permalink Normal View History

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
//+------------------------------------------------------------------+