fix(ai): drop the conv pooling stage - it reduced across filters, not time
FeedForwardConv emits POSITION-MAJOR output, matrix_o[out + window_out * i],
so one bar's window_out filter responses are contiguous and consecutive bars
sit window_out apart. Both pooling implementations (FeedForwardProof and
CPU_FeedForwardProof) slide FLAT over that buffer - pos = i * step, reducing
`window` CONSECUTIVE elements. On a position-major layout those neighbours
are different FILTERS of the same bar, never one filter across time.
At the shipped 3/2 the pool computed max(bar0_f0, bar0_f1, bar0_f2), then
max(bar0_f2, bar0_f3, bar0_f4), with every 8th window straddling a bar
boundary. So it collapsed unrelated feature detectors into whichever fired
hardest, passed gradient to that winner only, and halved the feature map
while doing it - all below every learnable layer, where nothing above can
recover it. The removed inputs' own labels ("3 Bars") show time-axis pooling
was the intent throughout.
Measured cost: CONV sat pinned at ~40% balanced accuracy for 510 eras with
Sell recall 0%, while plain MLPs on the same data reached 57-61%. HYBRID,
which also carried this stage, came second-worst of the batch-norm group.
Not fixable in the topology: pooling one filter across time needs a stride
of window_out BETWEEN samples within a window, which a consecutive-window
kernel cannot express at any window/step. That needs a stride-aware kernel
in Network.cl + WarriorCPU.cpp + WarriorDML.cpp and a DLL rebuild, and is
only worth doing if a conv front-end earns its place without downsampling
first - with 20 sliding positions there is little to gain by halving them.
ConvPoolWindow/ConvPoolStep and their enums are removed with it, along with
the |CP: fingerprint term added earlier today.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:28:44 -04:00
|
|
|
//+------------------------------------------------------------------+
|
2026-07-27 22:08:55 -04:00
|
|
|
//| Warrior_EA |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "..\Expert\ExpertSignalAIBase.mqh"
|
|
|
|
|
// wizard description start
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Description of the class |
|
|
|
|
|
//| Title=Signals of indicator 'CNN-LSTM AI' |
|
|
|
|
|
//| Type=SignalAdvanced |
|
|
|
|
|
//| Name=CNN-LSTM AI |
|
|
|
|
|
//| ShortName=HYB |
|
|
|
|
|
//| Class=CSignalHYBRID |
|
|
|
|
|
//| Page=signal_hybrid |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// wizard description end
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Class CSignalHYBRID. |
|
|
|
|
|
//| Purpose: a single fused CNN-LSTM signal that stacks Conv+Pool |
|
|
|
|
|
//| layers before the LSTM layer, then the shared dense taper. |
|
|
|
|
|
//| The base AI voting path still applies, so this module trades as |
|
|
|
|
|
//| one coherent signal instead of three loosely synchronized ones. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CSignalHYBRID : public CExpertSignalAIBase
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
virtual bool AddCustomLayers(CArrayObj *topology) override;
|
2026-07-30 15:20:30 -04:00
|
|
|
//--- AddCustomLayers below appends BOTH stages, conv first - so this topology's LSTM is fed the conv
|
|
|
|
|
//--- feature map rather than the raw input. Keep these in step with AddCustomLayers: they are what let
|
|
|
|
|
//--- the capacity budget size the recurrent block against its REAL fan-in (see LstmFanIn()).
|
|
|
|
|
virtual bool UsesConvStage(void) const override { return true; }
|
|
|
|
|
virtual bool UsesLstmStage(void) const override { return true; }
|
2026-07-27 22:08:55 -04:00
|
|
|
virtual ENUM_ACTIVATION HiddenLayerActivation(void) override { return TANH; }
|
|
|
|
|
public:
|
|
|
|
|
CSignalHYBRID(void);
|
|
|
|
|
};
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Constructor |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CSignalHYBRID::CSignalHYBRID(void)
|
|
|
|
|
{
|
fix(ensemble): per-member arrow namespaces; ConvLSTM rename; dialog in purge list
The ensemble chart UI had a shared-namespace defect that answered the user
question "what do the arrows represent?" with "a bug": all four members drew
arrows under the same WarSig_<bartime> object names, so the chart showed
whichever member rendered LAST, one member Neutral deleted another member Buy
at the same bar, each member init sweep wiped the arrows the previous member
had just restored, and SaveChartSignals - which rebuilds the sidecar by
SCANNING the chart - persisted every other member arrows into its own history
(the exact cross-model laundering its own header warns about, now happening
BETWEEN ensemble members).
Arrows are now namespaced per member (WarSig_PAI_, WarSig_CONV_, WarSig_LSTM_,
WarSig_HYB_): draw, delete, restore, prune, member init sweep, destructor
purge and the sidecar scan are all member-scoped, and the tooltip names the
model. Global purges keep matching the bare WarSig_ prefix, which covers all
member namespaces plus old-format leftovers from earlier builds.
Labels: the ensemble panel header no longer says "HYBRID ensemble" (HYBRID is
one member; the header is the ensemble) and the CONVLSTM member displays as
ConvLSTM instead of Hybrid. Its SHORT id stays HYB deliberately - it names the
model folder and changing it would orphan every model trained under that path.
Deinit: the alt-data mapping dialog namespace (WarriorAltMap_) joins
WarriorChartPrefixes, so both the OnInit purge and the deinit final sweep now
cover it - it was in neither list, so a dialog starved of its own Destroy()
left its controls on the chart permanently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 18:26:55 -04:00
|
|
|
//--- Display name follows the 2026-08-15 rename (AI_CONVLSTM: conv front-end + LSTM).
|
|
|
|
|
//--- The SHORT id stays "HYB" deliberately: it names the model folder
|
|
|
|
|
//--- (Neural Networks\State\HYB\) and changing it would orphan every model trained
|
|
|
|
|
//--- under the old path - a rename is not worth a forced retrain.
|
|
|
|
|
SetIdentity("ConvLSTM", "HYB");
|
2026-07-27 22:08:55 -04:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Conv + Pool front-end followed by an LSTM sequence layer. This |
|
|
|
|
|
//| matches the standalone CONV front-end exactly, then adds LSTM. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CSignalHYBRID::AddCustomLayers(CArrayObj *topology)
|
|
|
|
|
{
|
2026-07-29 00:38:05 -04:00
|
|
|
//--- "the standalone CONV front-end exactly, then adds LSTM" is now enforced by construction:
|
|
|
|
|
//--- both stages are the same code CSignalCONV and CSignalLSTM run.
|
fix(ai): drop the conv pooling stage - it reduced across filters, not time
FeedForwardConv emits POSITION-MAJOR output, matrix_o[out + window_out * i],
so one bar's window_out filter responses are contiguous and consecutive bars
sit window_out apart. Both pooling implementations (FeedForwardProof and
CPU_FeedForwardProof) slide FLAT over that buffer - pos = i * step, reducing
`window` CONSECUTIVE elements. On a position-major layout those neighbours
are different FILTERS of the same bar, never one filter across time.
At the shipped 3/2 the pool computed max(bar0_f0, bar0_f1, bar0_f2), then
max(bar0_f2, bar0_f3, bar0_f4), with every 8th window straddling a bar
boundary. So it collapsed unrelated feature detectors into whichever fired
hardest, passed gradient to that winner only, and halved the feature map
while doing it - all below every learnable layer, where nothing above can
recover it. The removed inputs' own labels ("3 Bars") show time-axis pooling
was the intent throughout.
Measured cost: CONV sat pinned at ~40% balanced accuracy for 510 eras with
Sell recall 0%, while plain MLPs on the same data reached 57-61%. HYBRID,
which also carried this stage, came second-worst of the batch-norm group.
Not fixable in the topology: pooling one filter across time needs a stride
of window_out BETWEEN samples within a window, which a consecutive-window
kernel cannot express at any window/step. That needs a stride-aware kernel
in Network.cl + WarriorCPU.cpp + WarriorDML.cpp and a DLL rebuild, and is
only worth doing if a conv front-end earns its place without downsampling
first - with 20 sliding positions there is little to gain by halving them.
ConvPoolWindow/ConvPoolStep and their enums are removed with it, along with
the |CP: fingerprint term added earlier today.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:28:44 -04:00
|
|
|
return AddConvStage(topology) && AddLstmStage(topology);
|
2026-07-27 22:08:55 -04:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|