Warrior_EA/Expert/Chart/AIBaseChartViewImpl.mqh
AnimateDread 5aec69fe4c feat(vote): replay pass rebuilds a deployed model's ladder without retraining
The previous commit persisted the tier ladder, which fixes this going
forward but did nothing for models whose .stats predates WST7 - they
still had to retrain to mint one. They never did. Every number a
converged model needs in order to vote is a pure function of weights
already on disk plus labels derivable from the chart, so replay them:

  stage 1  build the label cache   (existing chunked prebuild)
  stage 2  rescan history          (existing chunked rescan, deployed net)
  stage 3  score + rank + persist  (one walk over two arrays)

ScoreReplayFromCache() walks m_arrowSignalCache against
m_labelCacheBuy/Sell, fills the same m_oosTierFired/Hits and per-class
totals pass 3 fills, and hands them to RankTiersFromOos() - deliberately
feeding the existing ranker rather than reimplementing it. The shrinkage,
the chance reference and the module trust weight are subtle enough that a
second copy would drift, and a ladder measured by a slightly different
rule would be silently incomparable with every ladder training produced.

AdvanceDeployedRebuild() sequences the three stages off the timer. It has
to be a sequence: stages 1 and 2 are each minutes of work draining in
time-boxed slices, and stage 2's output is meaningless until stage 1 has
labels to score against. The previous version ran the rescan with no
labels at all, which is why it could only ever rebuild arrows and never
the ladder - the thing actually blocking the vote.

The result is written to .stats immediately. The failure being repaired
is state that lived in memory and was never written down; recomputing it
and not saving it would repeat that exactly.

Also routes every rescan completion through one hook, so there is a
single place that knows what a finished rescan means - republish for a
manual one, score and rank for a rebuild.

Compile-verified in _claude_stage: 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:01:24 -04:00

143 lines
8.8 KiB
MQL5

//+------------------------------------------------------------------+
//| Warrior_EA |
//| AnimateDread |
//| |
//| CAIBaseChartView bodies - needs the full signal declaration. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_CHART_AIBASECHARTVIEWIMPL_MQH
#define WARRIOR_CHART_AIBASECHARTVIEWIMPL_MQH
//+------------------------------------------------------------------+
//| EVERY METHOD HERE IS A FORWARD - same doctrine as |
//| Training\AIBaseTrainingDataImpl.mqh. It holds a BORROWED pointer |
//| and checks it on every call, because a NULL here would be a |
//| silent wrong answer (a blank panel, an arrow that never draws) |
//| rather than a crash - worse than failing loudly. |
//+------------------------------------------------------------------+
string CAIBaseChartView::Id(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataId() : ""; }
string CAIBaseChartView::FileName(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartFileName() : ""; }
string CAIBaseChartView::ArrowPrefix(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartArrowPrefix() : ""; }
ENUM_TIMEFRAMES CAIBaseChartView::Period(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartTimeframe() : PERIOD_CURRENT; }
int CAIBaseChartView::Digits(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartDigits() : 0; }
string CAIBaseChartView::DisplayNameForChart(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartDisplayName() : ""; }
bool CAIBaseChartView::ModelLoadedFromDisk(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartModelLoadedFromDisk() : false; }
bool CAIBaseChartView::TrainingComplete(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartTrainingComplete() : false; }
bool CAIBaseChartView::BothDirectionsTradeable(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartBothDirectionsTradeable() : true; }
int CAIBaseChartView::SignalClusterWindow(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.SignalClusterWindow() : 0; }
bool CAIBaseChartView::Stopping(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ShutdownRequested() : true; }
datetime CAIBaseChartView::BarTime(const int idx)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartBarTime(idx) : 0; }
double CAIBaseChartView::BarClose(const int idx)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartBarClose(idx) : 0.0; }
int CAIBaseChartView::HistoryBars(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.DataHistoryBars() : 0; }
int CAIBaseChartView::OutputNeuronsCount(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartOutputNeuronsCount() : 0; }
bool CAIBaseChartView::NetReady(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartNetReady() : false; }
int CAIBaseChartView::AvailableBars(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartAvailableBars() : 0; }
bool CAIBaseChartView::ResizeBuffers(const int barIndex)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartResizeBuffers(barIndex) : false; }
bool CAIBaseChartView::RefreshData(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartRefreshData() : false; }
int CAIBaseChartView::ServableBars(const int want, const string context)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartServableBars(want, context) : 0; }
void CAIBaseChartView::EnsureShadowNet(void)
{ if(CheckPointer(m_owner) != POINTER_INVALID) m_owner.ChartEnsureShadowNet(); }
bool CAIBaseChartView::ScoreBarForRescan(const int idx, double &rawSignal, double &adjustedSignal)
{
rawSignal = 0.0;
adjustedSignal = -2.0;
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartScoreBarForRescan(idx, rawSignal, adjustedSignal) : false;
}
ENUM_SIGNAL CAIBaseChartView::ToSignal(const double value)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartDoubleToSignal(value) : Neutral; }
int CAIBaseChartView::PredictionCacheSize(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartPredictionCacheSize() : 0; }
double CAIBaseChartView::PredictionAt(const int idx)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartPredictionAt(idx) : -2.0; }
void CAIBaseChartView::SetPredictionAt(const int idx, const double value)
{ if(CheckPointer(m_owner) != POINTER_INVALID) m_owner.ChartSetPredictionAt(idx, value); }
void CAIBaseChartView::ResizePredictionCache(const int size, const double fillValue)
{ if(CheckPointer(m_owner) != POINTER_INVALID) m_owner.ChartResizePredictionCache(size, fillValue); }
void CAIBaseChartView::PublishOverlaySnapshot(void)
{ if(CheckPointer(m_owner) != POINTER_INVALID) m_owner.OnChartRescanComplete(); }
long CAIBaseChartView::EraCount(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartEraCount() : 0; }
long CAIBaseChartView::CumIsTotal(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartCumIsTotal() : 0; }
long CAIBaseChartView::CumIsCorrect(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartCumIsCorrect() : 0; }
long CAIBaseChartView::CumOosTotal(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartCumOosTotal() : 0; }
long CAIBaseChartView::CumOosCorrect(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartCumOosCorrect() : 0; }
void CAIBaseChartView::OosTally(int &buyPredicted, int &sellPredicted, int &buyPredictedHits, int &sellPredictedHits)
{
buyPredicted = sellPredicted = buyPredictedHits = sellPredictedHits = 0;
if(CheckPointer(m_owner) != POINTER_INVALID)
m_owner.ChartOosTally(buyPredicted, sellPredicted, buyPredictedHits, sellPredictedHits);
}
string CAIBaseChartView::PassLabel(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartPassLabel() : ""; }
int CAIBaseChartView::PassProgressPct(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartPassProgressPct() : 0; }
int CAIBaseChartView::OosSplitPct(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartOosSplitPct() : 0; }
int CAIBaseChartView::OosSamples(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartOosSamples() : 0; }
void CAIBaseChartView::ClassCounts(int &predBuy, int &predSell, int &predNeutral,
int &trueBuy, int &trueSell, int &trueNeutral)
{
predBuy = predSell = predNeutral = trueBuy = trueSell = trueNeutral = 0;
if(CheckPointer(m_owner) != POINTER_INVALID)
m_owner.ChartClassCounts(predBuy, predSell, predNeutral, trueBuy, trueSell, trueNeutral);
}
void CAIBaseChartView::OosRecallPct(int &buyRecallPct, int &sellRecallPct)
{
buyRecallPct = sellRecallPct = -1;
if(CheckPointer(m_owner) != POINTER_INVALID)
m_owner.ChartOosRecallPct(buyRecallPct, sellRecallPct);
}
void CAIBaseChartView::OosLivePrecision(int &buyPrecPct, int &buyFired, int &sellPrecPct, int &sellFired)
{
buyPrecPct = sellPrecPct = -1;
buyFired = sellFired = 0;
if(CheckPointer(m_owner) != POINTER_INVALID)
m_owner.ChartOosLivePrecision(buyPrecPct, buyFired, sellPrecPct, sellFired);
}
double CAIBaseChartView::Forecast(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartForecast() : 0.0; }
double CAIBaseChartView::ErrorPct(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartErrorPct() : 0.0; }
double CAIBaseChartView::OosForecast(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartOosForecast() : 0.0; }
double CAIBaseChartView::OosErrorPct(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartOosErrorPct() : 0.0; }
double CAIBaseChartView::NetRecentAverageError(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartNetRecentAverageError() : 0.0; }
bool CAIBaseChartView::DisplayInference(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.ChartDisplayInference() : false; }
double CAIBaseChartView::LiveVoteContribution(const double signal)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.LiveVoteContribution(signal) : 0.0; }
double CAIBaseChartView::VoteCapableWeight(void)
{ return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.VoteCapableWeight() : 0.0; }
void CAIBaseChartView::PublishStatus(const string text, const bool force)
{ if(CheckPointer(m_owner) != POINTER_INVALID) m_owner.PublishStatus(text, force); }
#endif // WARRIOR_CHART_AIBASECHARTVIEWIMPL_MQH
//+------------------------------------------------------------------+