The purge was gated on m_arrowRestorePending, so it only ran when the .arrows
sidecar had something to load. Stale arrows do not come from the sidecar - MT5
persists chart objects in profiles\Charts\*\chart*.chr independently of it. A
chart carrying member arrows from a session where DrawUnfilteredSignals was ON
would therefore never be cleaned, because the sidecar it would have needed is
gone.
Currently inert: there are zero .arrows sidecars on disk. Which is also the
correction to the diagnosis in c5b9a1a's message - the '264 arrows restored' that
prompted it was a MISREAD of 'queued 264 combined-vote arrows', so member arrows
were never the cause of the reported clutter. The gate remains correct as
defence; it was not the fix. The window was.
Also silences the line when there is nothing to skip and nothing to clear: it
runs on every chart on every start, and six 'removed 0' lines are noise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Raising Signal_CooldownBars from 10 to 30 changed nothing. All six live charts
kept reporting a 10-bar window, because MT5 stores an input PER CHART in
profiles\Charts\*\chart*.chr and an already-attached EA ignores a changed
default entirely. This codebase already documents that trap, in the derived-
threshold comment in Training.mqh - and converting SignalClusterWindow from a
const to an input reintroduced the exact problem the const existed to avoid.
SignalCooldownOverrideBars (const, 30) now wins over the input; 0 hands control
back to the panel. Tunability per chart is kept, source-correctability is back.
Not applied when the input says OFF: an operator who switched the cooldown off
meant it, and silently re-enabling it from source would be the same surprise
pointed the other way.
ALSO gates the per-model arrow restore on DrawUnfilteredSignals. DrawObject()
returns early when the raw view is off, but AdvanceChartSignalRestore called
WarriorPlotSignalLevel DIRECTLY and never checked - so every restart repainted up
to MAX_PERSISTED_ARROWS per-model opinions per member, four members per chart, on
top of the combined-vote arrows. Same shape as the vote-arrow restore bug in
322c052: a restore path that does not obey the rule its own draw path does.
Stale arrows already on the chart are purged too, since MT5 persists objects in
the profile and nothing else would ever remove them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>