Warrior_EA/Expert/Chart/IChartView.mqh

105 lines
6.5 KiB
MQL5

refactor(chart): ChartUI is a real collaborator, not a raw-include partial (S2) Expert/AIBase/ChartUI.mqh was 869 lines of method bodies of CExpertSignalAIBase, #included after the class declaration - free to touch any of its ~500 members. First of the eleven AIBase/*.mqh partials to come out (fewest inbound edges - see the SOLID campaign session order), using the same view+adapter shape already proven for CTrainingDataView. CChartView (Expert/Chart/IChartView.mqh) is the abstract read/behaviour surface a chart-rendering collaborator needs - identity, bar/model access, the prediction cache, and the training/vote/meta scalars the panel and HUD line summarise. CAIBaseChartView is the adapter the signal owns and binds to itself (MQL5 gives a class exactly one base, so CExpertSignalAIBase cannot implement the view directly). CChartUI is the real collaborator: it owns the arrow-restore queue, the rescan queue/tally, the last-arrows-saved count and the purge-mismatch latch as its own fields (verified via grep to be touched nowhere else in Expert/), and reaches everything else - including StartChartSignalRescan, moved in from its old inline home in the header since it drives the exact same rescan state machine AdvanceChartSignalRescan drains - through the view. m_arrowSignalCache and m_signalClusterWindow stay on the signal: Training.mqh writes the cache directly every era and the training-data view already reads it, so moving it would mean rewriting Training.mqh's write sites too - out of scope here. CChartUI reaches it through four bounds-checked accessors instead of a raw member poke. All 10 public methods keep their exact signatures and become one-line forwards on the signal, so no other file's call sites change except Training.mqh's one era-end status refresh, which now reads RefreshStatusLabel() rather than reaching into CChartUI's now-private last-displayed-neuron cache directly. Verified structurally, not compiled (never compile - the operator does, in MetaEditor): brace balance checked on every touched/new file against HEAD, and the view/adapter/impl method lists cross-diffed to confirm all 59 accessors match 1:1 across the interface, the adapter declaration and the adapter body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 20:03:52 -04:00
//+------------------------------------------------------------------+
//| Warrior_EA |
//| AnimateDread |
//| |
//| A READ-ONLY VIEW of one model's identity, bars, model and |
//| training/vote state - everything the chart-rendering side needs. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_CHART_ICHARTVIEW_MQH
#define WARRIOR_CHART_ICHARTVIEW_MQH
//+------------------------------------------------------------------+
//| WHAT A CHART-SIDE COLLABORATOR IS ALLOWED TO SEE. |
//| |
//| Arrows, the status panel and the HUD line all need the same |
//| handful of things: who this model is, what a bar's time/price |
//| are, what the deployed net currently says, and the training/vote |
//| numbers the panel text summarises. Before this, CChartUI's |
//| predecessor (Expert\AIBase\ChartUI.mqh) was a raw-include body of |
//| CExpertSignalAIBase and reached straight into ~500 members. |
//| |
//| 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 |
//| CAIBaseChartView. Same shape as Training\ITrainingData.mqh. |
//+------------------------------------------------------------------+
class CChartView
{
public:
~CChartView(void) { }
//--- IDENTITY / CONFIG.
virtual string Id(void) = 0;
virtual string FileName(void) = 0;
virtual string ArrowPrefix(void) = 0;
virtual ENUM_TIMEFRAMES Period(void) = 0;
virtual int Digits(void) = 0;
virtual string DisplayNameForChart(void) = 0;
virtual bool ModelLoadedFromDisk(void) = 0;
virtual bool TrainingComplete(void) = 0;
virtual bool BothDirectionsTradeable(void) = 0;
virtual int SignalClusterWindow(void) = 0;
feat(signal): make the signal cooldown tunable, and add a hard any-direction gate The declustering the charts needed already existed - NmsLiveAccept, per-direction run-collapse plus cross-direction resolution plus strict alternation - and it was already set to 10 bars. It could not be TUNED: SignalClusterWindow was a compile- time const, so finding the right value needed a rebuild. That is the actual gap. Now three inputs, as enum dropdowns: Signal_CooldownScope per-direction, or a hard any-direction gate on top Signal_CooldownBars SCB_OFF..SCB_50, default 10 Signal_CooldownMinutes SCM_OFF..SCM_1440, overrides bars when set Minutes resolve against the CHART period and round UP, so a cooldown asked for in wall-clock is never silently shorter than requested and survives a timeframe change. SCB_/SCM_ prefixes are deliberately unique. M15/M30/M60 are ALREADY members of NF_LOOKBACK_PRESETS, and MQL5 binds a duplicated enum member to the first-declared enum silently - the obvious names would have compiled straight into the news filter's values. THE ANY-DIRECTION GATE IS ADDITIVE, NOT A REPLACEMENT, and the first cut of this had it backwards. Measured on the live log: the current rules draw 222 arrows over 4999 bars, while a BARE 10-bar cooldown permits up to 454 - because ALTERNATION is what declutters today, not the window. Swapping the rules out would have roughly doubled the clutter it was asked to remove. Layered, it can only ever suppress more. Suppressed bars still advance the per-direction last-SEEN cursors, so a run straddling the boundary does not restart as if it were fresh. Applied at all THREE sites that must agree - live inference, OOS pass-3 scoring and the chart renderer. Their own comments say why: an arrow set that does not obey the same rule as the traded set shows calls the EA would never take. Also corrects a stale comment that called this window "display only". It is not: when it suppresses, the live path zeroes the signal outright - no arrow, no vote, no position. Training never sees it, so these cost no retrain and are correctly absent from the fingerprint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 09:48:28 -04:00
//--- The cooldown SCOPE must reach the renderer too: the drawn set and the traded set obey one
//--- rule or the chart shows calls the EA would never take.
virtual SIGNAL_COOLDOWN_SCOPE SignalCooldownScope(void) = 0;
refactor(chart): ChartUI is a real collaborator, not a raw-include partial (S2) Expert/AIBase/ChartUI.mqh was 869 lines of method bodies of CExpertSignalAIBase, #included after the class declaration - free to touch any of its ~500 members. First of the eleven AIBase/*.mqh partials to come out (fewest inbound edges - see the SOLID campaign session order), using the same view+adapter shape already proven for CTrainingDataView. CChartView (Expert/Chart/IChartView.mqh) is the abstract read/behaviour surface a chart-rendering collaborator needs - identity, bar/model access, the prediction cache, and the training/vote/meta scalars the panel and HUD line summarise. CAIBaseChartView is the adapter the signal owns and binds to itself (MQL5 gives a class exactly one base, so CExpertSignalAIBase cannot implement the view directly). CChartUI is the real collaborator: it owns the arrow-restore queue, the rescan queue/tally, the last-arrows-saved count and the purge-mismatch latch as its own fields (verified via grep to be touched nowhere else in Expert/), and reaches everything else - including StartChartSignalRescan, moved in from its old inline home in the header since it drives the exact same rescan state machine AdvanceChartSignalRescan drains - through the view. m_arrowSignalCache and m_signalClusterWindow stay on the signal: Training.mqh writes the cache directly every era and the training-data view already reads it, so moving it would mean rewriting Training.mqh's write sites too - out of scope here. CChartUI reaches it through four bounds-checked accessors instead of a raw member poke. All 10 public methods keep their exact signatures and become one-line forwards on the signal, so no other file's call sites change except Training.mqh's one era-end status refresh, which now reads RefreshStatusLabel() rather than reaching into CChartUI's now-private last-displayed-neuron cache directly. Verified structurally, not compiled (never compile - the operator does, in MetaEditor): brace balance checked on every touched/new file against HEAD, and the view/adapter/impl method lists cross-diffed to confirm all 59 accessors match 1:1 across the interface, the adapter declaration and the adapter body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 20:03:52 -04:00
//--- Stop requested, or the program is unloading - same wrap as CTrainingDataView::Stopping().
virtual bool Stopping(void) = 0;
//--- BARS / MODEL. m_Time/m_Close are protected stdlib series the adapter cannot expose directly.
virtual datetime BarTime(const int idx) = 0;
virtual double BarClose(const int idx) = 0;
virtual int HistoryBars(void) = 0;
virtual int OutputNeuronsCount(void) = 0;
virtual bool NetReady(void) = 0;
//--- Bars() on the chart's OWN period (deliberately PERIOD_CURRENT, not this model's Period() -
//--- same as the code this replaces), for sizing a manual rescan's lookback.
virtual int AvailableBars(void) = 0;
virtual bool ResizeBuffers(const int barIndex) = 0;
virtual bool RefreshData(void) = 0;
virtual int ServableBars(const int want, const string context) = 0;
virtual void EnsureShadowNet(void) = 0;
//--- ONE bar through the deployed (shadow-preferred) net: builds the feature window, forwards,
//--- and hands back both the raw argmax-basis signal and the prior-corrected one - the exact pair
//--- AdvanceChartSignalRescan needs, without handing the net itself to a chart-rendering object.
//--- False = the window could not be built (bar skipped, not scored).
virtual bool ScoreBarForRescan(const int idx, double &rawSignal, double &adjustedSignal) = 0;
virtual ENUM_SIGNAL ToSignal(const double value) = 0;
//--- PREDICTION CACHE (m_arrowSignalCache). Stays signal-owned - Training.mqh writes it directly
//--- every era and the training-data view already reads it for the baseline comparator - so this
//--- is a bounds-checked window onto shared state, not a copy.
virtual int PredictionCacheSize(void) = 0;
virtual double PredictionAt(const int idx) = 0;
virtual void SetPredictionAt(const int idx, const double value) = 0;
virtual void ResizePredictionCache(const int size, const double fillValue) = 0;
fix(chart): a deployed model rescans history to rebuild its vote arrows The sidecar added in 484a9d8 restores the vote arrows from the previous session - but there was no previous session to restore from, and a deployed ensemble could never produce one. The overlay that draws the vote layer replays each member's m_overlaySigSnap, published in exactly one place: RankTiersFromOos, at pass-3 completion. A converged model runs no further eras. So after a restart every member's snapshot was empty, would never fill, the sweep had nothing to replay and the chart stayed blank permanently - no route back by any path. The chart rescan is the route: it runs the DEPLOYED net forward over history and rebuilds the per-bar cache, which is the same quantity pass 3 produces, obtained without training. It already existed for the panel's Show-Signals button; it just never handed its result to the overlay, so on the default filtered view a rescan rebuilt only the RAW per-member layer - the one that is hidden - and appeared to do nothing. - PublishOverlaySnapshotFromCache() extracted from RankTiersFromOos, so the era end and a completed rescan publish through one implementation. - A completed rescan now calls it, which also arms the sweep. - PollTraining auto-arms one rescan for a model that is converged, has no snapshot, and is on the filtered view. One-shot: a model that legitimately calls Neutral everywhere must not rescan forever chasing a snapshot that is correctly empty. On the timer, not in OnInit - it is a full feedForward per bar over up to 5000 bars and drains in the same time-boxed slices as a manual rescan. Together with the sidecar this closes both halves: the rescan covers the first session and any chart whose file was lost or invalidated by a threshold change; the sidecar covers every session after one is saved. Compile-verified in _claude_stage: 0 errors, 0 warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 13:11:36 -04:00
//--- Hand the cache the rescan just rebuilt to the overlay as this member's snapshot, and mark the
//--- member ready for a sweep. The rescan is the only way a DEPLOYED model can produce one - it
//--- runs no further eras, and the era end is the only other publisher.
virtual void PublishOverlaySnapshot(void) = 0;
refactor(chart): ChartUI is a real collaborator, not a raw-include partial (S2) Expert/AIBase/ChartUI.mqh was 869 lines of method bodies of CExpertSignalAIBase, #included after the class declaration - free to touch any of its ~500 members. First of the eleven AIBase/*.mqh partials to come out (fewest inbound edges - see the SOLID campaign session order), using the same view+adapter shape already proven for CTrainingDataView. CChartView (Expert/Chart/IChartView.mqh) is the abstract read/behaviour surface a chart-rendering collaborator needs - identity, bar/model access, the prediction cache, and the training/vote/meta scalars the panel and HUD line summarise. CAIBaseChartView is the adapter the signal owns and binds to itself (MQL5 gives a class exactly one base, so CExpertSignalAIBase cannot implement the view directly). CChartUI is the real collaborator: it owns the arrow-restore queue, the rescan queue/tally, the last-arrows-saved count and the purge-mismatch latch as its own fields (verified via grep to be touched nowhere else in Expert/), and reaches everything else - including StartChartSignalRescan, moved in from its old inline home in the header since it drives the exact same rescan state machine AdvanceChartSignalRescan drains - through the view. m_arrowSignalCache and m_signalClusterWindow stay on the signal: Training.mqh writes the cache directly every era and the training-data view already reads it, so moving it would mean rewriting Training.mqh's write sites too - out of scope here. CChartUI reaches it through four bounds-checked accessors instead of a raw member poke. All 10 public methods keep their exact signatures and become one-line forwards on the signal, so no other file's call sites change except Training.mqh's one era-end status refresh, which now reads RefreshStatusLabel() rather than reaching into CChartUI's now-private last-displayed-neuron cache directly. Verified structurally, not compiled (never compile - the operator does, in MetaEditor): brace balance checked on every touched/new file against HEAD, and the view/adapter/impl method lists cross-diffed to confirm all 59 accessors match 1:1 across the interface, the adapter declaration and the adapter body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 20:03:52 -04:00
//--- PANEL / HUD SCALARS. Read-only summaries of training, vote and meta-gate state.
virtual long EraCount(void) = 0;
virtual long CumIsTotal(void) = 0;
virtual long CumIsCorrect(void) = 0;
virtual long CumOosTotal(void) = 0;
virtual long CumOosCorrect(void) = 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
virtual void OosTally(int &buyPredicted, int &sellPredicted, int &buyPredictedHits, int &sellPredictedHits) = 0;
refactor(chart): ChartUI is a real collaborator, not a raw-include partial (S2) Expert/AIBase/ChartUI.mqh was 869 lines of method bodies of CExpertSignalAIBase, #included after the class declaration - free to touch any of its ~500 members. First of the eleven AIBase/*.mqh partials to come out (fewest inbound edges - see the SOLID campaign session order), using the same view+adapter shape already proven for CTrainingDataView. CChartView (Expert/Chart/IChartView.mqh) is the abstract read/behaviour surface a chart-rendering collaborator needs - identity, bar/model access, the prediction cache, and the training/vote/meta scalars the panel and HUD line summarise. CAIBaseChartView is the adapter the signal owns and binds to itself (MQL5 gives a class exactly one base, so CExpertSignalAIBase cannot implement the view directly). CChartUI is the real collaborator: it owns the arrow-restore queue, the rescan queue/tally, the last-arrows-saved count and the purge-mismatch latch as its own fields (verified via grep to be touched nowhere else in Expert/), and reaches everything else - including StartChartSignalRescan, moved in from its old inline home in the header since it drives the exact same rescan state machine AdvanceChartSignalRescan drains - through the view. m_arrowSignalCache and m_signalClusterWindow stay on the signal: Training.mqh writes the cache directly every era and the training-data view already reads it, so moving it would mean rewriting Training.mqh's write sites too - out of scope here. CChartUI reaches it through four bounds-checked accessors instead of a raw member poke. All 10 public methods keep their exact signatures and become one-line forwards on the signal, so no other file's call sites change except Training.mqh's one era-end status refresh, which now reads RefreshStatusLabel() rather than reaching into CChartUI's now-private last-displayed-neuron cache directly. Verified structurally, not compiled (never compile - the operator does, in MetaEditor): brace balance checked on every touched/new file against HEAD, and the view/adapter/impl method lists cross-diffed to confirm all 59 accessors match 1:1 across the interface, the adapter declaration and the adapter body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 20:03:52 -04:00
virtual string PassLabel(void) = 0;
virtual int PassProgressPct(void) = 0;
virtual int OosSplitPct(void) = 0;
virtual int OosSamples(void) = 0;
virtual void ClassCounts(int &predBuy, int &predSell, int &predNeutral,
int &trueBuy, int &trueSell, int &trueNeutral) = 0;
virtual void OosRecallPct(int &buyRecallPct, int &sellRecallPct) = 0;
virtual void OosLivePrecision(int &buyPrecPct, int &buyFired, int &sellPrecPct, int &sellFired) = 0;
virtual double Forecast(void) = 0; // dForecast
virtual double ErrorPct(void) = 0; // dError
virtual double OosForecast(void) = 0; // dOosForecast
virtual double OosErrorPct(void) = 0; // dOosError
virtual double NetRecentAverageError(void) = 0;
feat(panel): one live vote line, no stale era count, no per-model HUD Two chart-display fixes reported after watching a converged 4-model ensemble: the ensemble panel's trailing "(era 69, 4 models, DEPLOYING)" was frozen at whatever era the ensemble happened to deploy on, and the separate top-right HUD (one line per model, raw B/S/N + weight + era + error) was clutter once the vote itself is what matters. Root cause of the freeze: g_ensembleVoteLine is written once per era, at pass-3 completion. A deployed/converged ensemble runs no further eras (ScheduleTrainingIfNeeded's trainingComplete branch skips Train() entirely), so that line could never update again - the era count and "DEPLOYING" marker were permanent set-dressing from the deploying era, not a live reading. - EnsembleScoreCombinedVote() drops the era/DEPLOYING tail once g_ensDeployApproved - nothing left there worth freezing. - UpdateVoteReadout() (the aggregate "VOTE ..." line, previously its own top-right chart object) now writes g_liveVoteLine instead of drawing anything. Both status-label builders - PublishEnsembleStatus for the ensemble panel, PublishStatus's choke point for the solo panel - append it as one line, refreshed every tick/timer exactly as the old HUD was, so the live vote replaces the frozen era tail in the same visual slot. - RefreshVoteReadout()'s per-member loop (DisplayHudLine, one ObjectLabel per model) is deleted outright rather than folded in - the operator asked for the aggregate only, "without telling me each individual network". Follow-on dead-code removal, since DisplayHudLine was the only caller: the DispProb/DispSignal/MetaGateArmedNow/MetaHasScore/ MetaLastP/MetaLastBe/MetaApproved/MetaVetoed leg of IChartView (and its AIBaseChartView/AIBaseChartViewImpl/ExpertSignalAIBase forwards) had no other reader. The underlying data survives untouched - m_metaTelemetry is still populated live by SignalMETA.mqh, m_dispSignal still feeds ProspectiveVote - only the chart-view forwarding that existed solely to reach the deleted HUD is gone. Compile: 0 errors, 0 warnings (stage). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:27:41 -04:00
//--- Throttled, side-effect-free forward of the CURRENT decision bar. See the signal's own
//--- declaration comment (AIBase\Inference.mqh).
refactor(chart): ChartUI is a real collaborator, not a raw-include partial (S2) Expert/AIBase/ChartUI.mqh was 869 lines of method bodies of CExpertSignalAIBase, #included after the class declaration - free to touch any of its ~500 members. First of the eleven AIBase/*.mqh partials to come out (fewest inbound edges - see the SOLID campaign session order), using the same view+adapter shape already proven for CTrainingDataView. CChartView (Expert/Chart/IChartView.mqh) is the abstract read/behaviour surface a chart-rendering collaborator needs - identity, bar/model access, the prediction cache, and the training/vote/meta scalars the panel and HUD line summarise. CAIBaseChartView is the adapter the signal owns and binds to itself (MQL5 gives a class exactly one base, so CExpertSignalAIBase cannot implement the view directly). CChartUI is the real collaborator: it owns the arrow-restore queue, the rescan queue/tally, the last-arrows-saved count and the purge-mismatch latch as its own fields (verified via grep to be touched nowhere else in Expert/), and reaches everything else - including StartChartSignalRescan, moved in from its old inline home in the header since it drives the exact same rescan state machine AdvanceChartSignalRescan drains - through the view. m_arrowSignalCache and m_signalClusterWindow stay on the signal: Training.mqh writes the cache directly every era and the training-data view already reads it, so moving it would mean rewriting Training.mqh's write sites too - out of scope here. CChartUI reaches it through four bounds-checked accessors instead of a raw member poke. All 10 public methods keep their exact signatures and become one-line forwards on the signal, so no other file's call sites change except Training.mqh's one era-end status refresh, which now reads RefreshStatusLabel() rather than reaching into CChartUI's now-private last-displayed-neuron cache directly. Verified structurally, not compiled (never compile - the operator does, in MetaEditor): brace balance checked on every touched/new file against HEAD, and the view/adapter/impl method lists cross-diffed to confirm all 59 accessors match 1:1 across the interface, the adapter declaration and the adapter body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 20:03:52 -04:00
virtual bool DisplayInference(void) = 0;
virtual double LiveVoteContribution(const double signal) = 0;
virtual double VoteCapableWeight(void) = 0;
//--- STATUS.
virtual void PublishStatus(const string text, const bool force) = 0;
};
#endif // WARRIOR_CHART_ICHARTVIEW_MQH
//+------------------------------------------------------------------+