Commit graph Warrior_EA/Expert/Chart/VoteArrows.mqh
Author SHA1 Message Date
AnimateDread
781ae3a702 perf(deinit): I/O-free chart cleanup, dead-panel purge, skip clean weight saves
The 18:23 terminal close (20260825.log) killed two of six charts inside
OnDeinit: they printed "shutting down" then nothing for 5.9 s until
"Abnormal termination", stranding ~700 objects each - including the one
family no prefix sweep can reach, the control panel (CAppDialog names
its 15 objects <numeric instance id><control>, and a re-attach mints a
new id, so a killed panel is a permanent ghost; XTIUSD carried one
across sessions). The stall sat in the two file writes that preceded
all visible cleanup while the four sibling charts flooded the same
2013-era disk - the ~4x18MB-per-chart shutdown weight saves.

Three changes:

1. OnDeinit touches no file until the chart is clean. CVoteArrowStore
   splits Save() into Snapshot() (the chart scan, in memory) and
   WriteSnapshot() (the disk half, consuming). New order: status label,
   vote-arrow snapshot, prefix sweep, panel destroy - all object ops -
   then member sidecars, final sweep, timings, and only then the
   visibility file, the vote-arrow write and the weight saves.

2. PurgeOrphanedPanelObjects() at OnInit: deletes numeric-prefix
   CAppDialog ghosts by name (6 chrome + 9 buttons), qualifying a
   prefix only when >=4 of OUR button names carry it, so a foreign
   dialog sharing stock chrome names is never touched.

3. m_netDirty: set by every net mutation (both backProp sites, both
   RestoreWeights sites, online learning conservatively, panel reset),
   cleared only on a successful Net.Save. Shutdown AND the per-bar
   autosave now skip the ~18MB write when the net is provably unchanged
   - for converged ensembles that is every save - which removes the
   very flood that starved the sibling charts. .stats still writes
   every time (small; carries the vote record and calibration). A
   skipped save leaves the .nnw header dtStudied stale, which is the
   already-handled attach-after-offline-gap case.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 18:46:51 -04:00
AnimateDread
484a9d8b0f fix(panel,arrows): one deploy predicate, a deployed-only readout, and persist the vote arrows
Four reported symptoms, three of them one root cause: the ensemble's
certified record was session-scoped and written ONLY at pass-3
completion. A deployed ensemble runs no further eras, so every restart
lost the aggregate win rate, the aggregate panel line and the overlay
snapshots - and could never regenerate them, because regeneration only
happens at an era end that will never come.

THE SELF-CONTRADICTION. Member rows read "Live - learning from new bars"
(from m_trainingComplete) while the line under them read "training, not
tradable yet" (from `prospective`, which means "this number came from
ProspectiveVote() rather than a real Direction() call" - what happens on
any bar where every member abstains, and which says nothing whatever
about training state). Both now resolve through one predicate:
WarriorChartModelsDeployed(), fed by members publishing their own state
on the same slot and cadence as their vote. Adds a third verdict word,
"armed (bar still open)", for a deployed model on a prospective
recompute - the case that used to claim it was training.

DEPLOYED PANEL. Once every published model is converged the per-member
rows are dropped: what ships is the aggregate vote win rate, the live
vote, and the verdict. While training the rows stay - they are the only
way a collapsed or lagging member is visible, since a collapsed member
abstains and so is invisible in the aggregate by construction.

ACCURACY NOW RESPECTS THE ENTRY THRESHOLD. The panel's "precision 65%"
came from m_cumOosCorrect/m_cumOosTotal, which counts every bar a model
called Buy or Sell - threshold-blind, and per-model rather than
per-vote. The correct number already existed (votePrecPct: bars where
|vote| >= threshold and the direction policy allows) and is now what the
panel shows, with the threshold named in the text because the number is
meaningless without it.

VOTE ARROWS PERSIST. With DrawUnfilteredSignals off - the default - the
chart shows SIG_VOTE_PREFIX arrows, and nothing saved them:
CChartUI's .arrows sidecar is member-scoped and never saw that layer.
New CVoteArrowStore mirrors them to a chart-keyed sidecar and restores
them progressively at init, on the same budgeted non-blocking path.
The header stores the open/close thresholds; a mismatch on load DISCARDS
the arrows rather than redrawing a picture of a strategy no longer
configured - stale arrows are worse than none, because none is visibly
empty and stale is confidently wrong.

Also: .stats bumped to WST7 carrying the ensemble record (guarded on
threshold match, most-complete-copy-wins), and the loader's version
tests collapsed from an or-chain to ">=" - the magics are ASCII 'WST1'..
'WST7' so they are already ordered, and a missed arm in that chain reads
the NEXT field's bytes into this one, which fails as plausible numbers
rather than as an error.

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

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