forked from animatedread/Warrior_EA
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
213b3aac15 |
fix(chart): the reconciliation never ran - it was hooked to a sweep deployed charts do not do
cooldown-recon put the chart-wide cooldown at the end of the overlay sweep. It executed ZERO times. This store's own header already said why: the sweep 're-arms only when an era ends. A DEPLOYED ensemble runs no further eras'. Five of six charts were deployed, so there were zero 'Filtered view: swept' lines in the entire session while the saved files still held 148 same-side pairs under 30 bars on XTIUSD. Moved to the completion of the progressive vote-arrow restore, which runs on every chart including deployed ones. The restore thinning alone was never going to be enough either: MT5 persists chart objects in profiles\Charts\*\chart*.chr, so arrows drawn under an older window are ALREADY on the chart when the process starts, and a freshly-thinned restore just adds to them. Two correctly-thinned sets still union into clusters. The chart is the only authority. Same construction as before: OBJ_TREND only (the line is the canonical half of a mark, matching Snapshot()), sorted by time first because object order is not time order, and the gap>0 guard so a mis-ordered set fails visibly by keeping rather than silently by deleting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0dda31534e |
fix(chart): purge stale member arrows even when there is no sidecar to restore
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> |
||
|
|
c5b9a1ad10 |
fix(signal): a changed input default cannot reach an already-attached EA
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
|
||
|
|
322c052a65 |
fix(chart): the persisted vote-arrow restore put back every arrow the cooldown removed
Clutter remained after cooldown-v3 because there is a FOURTH producer of
SIG_VOTE_PREFIX arrows: CVoteArrowStore, which replays a .votearrows file and
draws into the SAME object names as the overlay. Replaying a file written before
the cooldown existed therefore resurrects exactly the arrows the prune deleted.
On a DEPLOYED chart that is the entire arrow set. The store's own header says
why: the overlay re-sweep "re-arms only when an era ends. A DEPLOYED ensemble
runs no further eras" - which is the reason this store exists at all, and also
the reason nothing would ever have removed those arrows again.
The restore now thins to the cooldown at Load(), before the progressive draw is
armed, so it is idempotent: a file already written from a cooled chart passes
through untouched, an older one is corrected once.
IT SORTS BY TIME FIRST, AND THAT IS NOT OPTIONAL. Snapshot() walks
ObjectsTotal(), so the record is in OBJECT order - its own comment says so, and
the existing MAX_KEPT trim already sorts a copy for exactly this reason. Applying
a spacing rule to an unsorted record yields negative gaps, and a negative gap is
inside any window: that is the bug that wiped 272 of 273 arrows in
|
||
|
|
6308a19f27 |
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
b92e233b88 |
fix(chart): a deployed model rescans history to rebuild its vote arrows
The sidecar added in
|
||
|
|
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> |
||
|
|
1baa13c5b4 |
refactor(meta): remove meta-labeling entirely - RETRAIN-NEUTRAL
~2,300 lines. META had real, repeatedly measured ranking skill and ZERO
operating points that ever cleared break-even (0/350 H1 eras, 1/999 H4
pre-2-sigma, 0/8 pooled fitted points). The clinching arithmetic was edge x
width = 0.095 ATR/trade against spread 0.099 ATR/trade, and the
dose-response showed the high-conviction tail is temporally unstable -
the precision-vs-threshold slope flips sign between calib and test on 3 of
4 symbols, so no ex-ante threshold rule exists. It shipped default-off and
never gated a live entry. The self-measured tier weights are what actually
rank the vote, and all six H4 instruments converged on them alone.
RETRAIN-NEUTRAL, and that is the property that made this safe:
- The weights fingerprint emitted "|TGT:META2" or "|TGT:SWG1" from an
if/else. Every direction model already took the SWG1 arm, so
collapsing it to an unconditional append is byte-identical. No .nnw or
.cfg is orphaned or re-keyed.
- NetInputWidth() lost its "+ MetaDescWidth()" term. MetaDescWidth()
returned 0 for every direction model, so the input layer is unchanged.
- DbLegacyAiSlot()'s slot 5 was reachable only with all four Use_* NNs
off AND meta on - a config that never shipped. Every existing .db keeps
its filename.
Deleted outright: Signals/SignalMETA.mqh, Expert/Trading/MetaGate.mqh (the
directory is now empty), Expert/Training/{MetaCorpus,MetaCandidateStore,
MetaFamilies}.mqh, Tests/Test_MetaFamilies.mq5, Meta_Labeling_Design.md.
Unwound in place, the delicate part: Training.mqh carried four
IsMetaTarget() branches whose else-arm WRAPPED the direction body (pass 1
queueing, pass 2 backprop, pass 2.5 calibration, pass 3 OOS scoring). Each
wrapper is removed and the direction body promoted back to its original
nesting - the bodies were never re-indented when the wrappers were added,
so the promoted code is byte-identical to what ran before META existed.
Also gone: the ensemble verdict's meta-veto replay and its
approved/vetoed/unscored counters, the per-family/per-side OOS
decomposition arrays, the m_isTrainQueueCand parallel queue and its
lockstep shuffle, and the S2 era report.
Also removed: the CMetaGate abstraction and the live CheckOpenPosition
veto; m_gates plus AddFilter's non-voter routing and IsVotingSignal()
(META was the only non-voting child, so m_gates was always empty);
m_parentSignal/SetParentSignal (existed only to reach the root's gate);
SweepPrepare/SweepPrepareIndicator (only caller was the corpus sweep);
IsMetaTarget() from all four view interfaces and their adapters;
Use_MetaLabeling, EnableMETA, Meta_ExportDataset, m_trainTarget.
EvalShift is KEPT - HistoricalNetVote() uses it for the filtered overlay,
not just the corpus sweep; only its comment changed. The 2-output softmax
arm in NetForward.mqh is kept too: it costs nothing and is the reusable
binary-head path, now commented as unclaimed rather than as META's.
Compile-verified in _claude_stage: 0 errors, 0 warnings, matching the
pre-edit baseline.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
b5d34a82b4 |
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> |
||
|
|
8f2164698b |
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> |
||
|
|
ada8ddac22 | refactor(chart): rename methods for consistency and clarity in CAIBaseChartView and ExpertSignalAIBase | ||
|
|
053d704a84 |
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> |