The //| box blocks were excluded from 0b06f8e and 5efdb48 and were what
remained: 160 of them ran to 10+ lines, the longest to 88. Compressed to their
leading topic sentences - 5 lines for a function header, 8 for a file header -
keeping the box format and the standard MQL5 name/author lines verbatim.
Verified at the BYTE level this time, across every in-scope file: the list of
non-comment lines is byte-identical to HEAD and braces balance. The first check
compared a locale-decoded 'git show' against a UTF-8 read and flagged 25 files
that had not changed at all - every BOM and every non-ASCII line mismatched.
47,696 -> 40,665 lines in scope; comment share 38% -> 26%.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The control panel drove training by looping g_aiSignals[] - a
hand-maintained, MAX_AI_SIGNALS-capped, AI-only registry that had
already dropped an ensemble member on the floor once (609be10). A model
missing from it still trains and still votes, it just cannot be paused,
stopped, deployed or reset, and every button label is computed from the
same short list, so the panel described one set of models while acting
on another. Classic signals could not respond to a panel action at all.
Commands now walk the signal tree CExpert already owns:
Expert.DispatchSignalCommand(cmd) -> root signal -> every filter,
recursively, returning how many actually acted.
CExpertSignalCustom carries the seam (OnSignalCommand / HasSignalTrait,
both no-ops by default), so a classic signal opts in by overriding two
methods and needs no registration and no cap. CExpertSignalAIBase
implements the training commands over its existing Pause/Stop/Deploy/
Reset methods - the behaviour is unchanged, only its reach reported.
Button labels ask the same tree via CountSignalTrait, with
SIGTRAIT_TRAINABLE as an explicit denominator: "all paused" is
meaningless without knowing how many could be paused. Pause/Stop resolve
their toggle direction ONCE in the EA and hand every model the same
plain command, instead of each re-deriving the direction from its own
local state - which is how a mixed set ends up half paused. The alerts
now report the count acted on rather than assuming it.
Two dispatch bugs found on the way, both from a database guard copied
onto event delivery: CExpertSignalCustom::OnTickHandler and
::OnChartEventHandler each skipped any filter whose GetFilterID() is
"NULL". That id is a DB folder name, and CSignalNewsFilter,
CSignalSessionFilter and CSignalRiskGuard never set one - so all three
were silently receiving neither ticks nor chart events. The guard stays
where it belongs, on the paths that write pattern tables.
ENUM_CP_ACTION moves to Enumerations\GlobalEnums.mqh (now include-
guarded) because the Expert bases have to name it and the panel is
included long after them.
The AI-only lifecycle loops - PollTraining, the weight autosave,
AltDataReload, OnDeinit's shutdown cascade - still use g_aiSignals[] and
are untouched here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>