forked from animatedread/Warrior_EA
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>
148 lines
9 KiB
MQL5
148 lines
9 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; }
|
|
SIGNAL_COOLDOWN_SCOPE CAIBaseChartView::SignalCooldownScope(void)
|
|
{
|
|
return (CheckPointer(m_owner) != POINTER_INVALID) ? m_owner.SignalCooldownScope()
|
|
: SIGNAL_COOLDOWN_PER_DIRECTION;
|
|
}
|
|
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
|
|
//+------------------------------------------------------------------+
|