Warrior_EA/Expert/Training/AIBaseTrainingDataImpl.mqh

112 lines
4.9 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 |
//| |
//| CAIBaseTrainingData bodies - needs the full signal declaration. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_TRAINING_AIBASETRAININGDATAIMPL_MQH
#define WARRIOR_TRAINING_AIBASETRAININGDATAIMPL_MQH
//+------------------------------------------------------------------+
//| EVERY METHOD HERE IS A FORWARD, and that is the whole point. |
//| |
//| The bounds tests, the sentinels and the "has the gate scored yet" |
//| rule all live once, next to the data, in the signal's published |
//| read API. This file only maps that API onto CTrainingDataView, so |
//| a collaborator can be written, read and replaced without ever |
//| naming CExpertSignalAIBase. |
//| |
//| It holds a BORROWED pointer. The signal owns the adapter, so the |
//| owner outlives it by construction - but every call still checks, |
//| because a NULL here would be a silent wrong answer rather than a |
//| crash, and a diagnostic that quietly reports nothing is worse |
//| than one that fails loudly. |
//+------------------------------------------------------------------+
int CAIBaseTrainingData::HistoryBars(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataHistoryBars() : 0;
}
int CAIBaseTrainingData::FeaturesPerBar(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataFeaturesPerBar() : 0;
}
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 CAIBaseTrainingData::LabelResolutionBars(void)
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
{
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
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataLabelResolutionBars() : 1;
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
}
int CAIBaseTrainingData::PurgeBars(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataPurgeBars() : 0;
}
int CAIBaseTrainingData::CalibrationHiIndex(const int totalIter, const int oosCutoff)
{
return (CheckPointer(m_owner) != POINTER_INVALID)
? m_owner.DataCalibrationHiIndex(totalIter, oosCutoff) : 0;
}
bool CAIBaseTrainingData::HasLabel(const int bar)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataHasLabel(bar) : false;
}
bool CAIBaseTrainingData::IsBuyLabel(const int bar)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataIsBuyLabel(bar) : false;
}
bool CAIBaseTrainingData::IsSellLabel(const int bar)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataIsSellLabel(bar) : false;
}
bool CAIBaseTrainingData::RowFeatures(const int bar, const int width, double &x[])
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.BaselineRowFeatures(bar, width, x) : false;
}
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
bool CAIBaseTrainingData::DirectionalCall(const int bar, bool &isBuy, double &magnitude)
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
{
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
isBuy = false;
magnitude = 0.0;
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataDirectionalCall(bar, isBuy, magnitude) : false;
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
}
string CAIBaseTrainingData::Id(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataId() : "";
}
bool CAIBaseTrainingData::IsEnsembleMember(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataIsEnsembleMember() : false;
}
int CAIBaseTrainingData::EnsembleIndex(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataEnsembleIndex() : -1;
}
bool CAIBaseTrainingData::GateReference(double &precPct, int &calls, double &chancePct)
{
precPct = chancePct = -1.0;
calls = 0;
return (CheckPointer(m_owner) != POINTER_INVALID)
? m_owner.DataGateReference(precPct, calls, chancePct) : false;
}
double CAIBaseTrainingData::EffectiveSampleSize(const double rawN)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataEffectiveSampleSize(rawN) : rawN;
}
bool CAIBaseTrainingData::Stopping(void)
{
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ShutdownRequested() : true;
}
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
//+------------------------------------------------------------------+
//| One feature window into a plain double[] - the shape both Alglib |
//| predictors take. Fails exactly where the net's own path fails. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::BaselineRowFeatures(const int bar, const int width, double &x[])
{
if(!BuildFeatureWindow(bar) || TempData.Total() < width)
return false;
for(int f = 0; f < width; f++)
{
double v = TempData.At(f);
if(!MathIsValidNumber(v))
return false;
x[f] = v;
}
return true;
}
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
#endif // WARRIOR_TRAINING_AIBASETRAININGDATAIMPL_MQH
//+------------------------------------------------------------------+