Warrior_EA/AI_NETWORK.md
AnimateDread d7eea325fb refactor(ai): extract Layer.mqh and deduplicate AI config
- Moves CLayer neuron construction to AI/Impl/Layer.mqh to keep Network.mqh clean
- Unifies four previously duplicated architecture initialisation blocks (MLP/CONV/LSTM/HYBRID) into a single shared function
- Eliminates risk of behavioural drift where one architecture missed a setter, causing mismatched feature sets or targets
2026-08-01 11:27:28 -04:00

5.7 KiB

AI/ — neural network subsystem

The engine behind the four AI signal modules (CSignalPAI, CSignalCONV, CSignalLSTM, CSignalHYBRID). It is a from-scratch MQL5 network — no external ML runtime — with four interchangeable compute tiers behind one interface.

File layout

AI\Network.mqh is the entry point and the only file anything outside AI\ includes. It holds declarations only: the nine core classes, the compile-time constants, and the nested include chain that orders them. Each nested #include sits exactly where its base class becomes visible, so that order is a dependency graph, not a preference — do not reorder it.

file holds
Network.mqh class declarations, tuning constants, include chain
Network.cl OpenCL kernels (see below)
NeuronPrimitives.mqh CConnection / CArrayCon — weight storage
NeuronCPU.mqh CNeuron — pure-MQL5 dense neuron
NeuronDirectML.mqh CDirectMLMy — DirectML + CPU-DLL bridge (#imported DLLs)
ArrayLayer.mqh CArrayLayer — the layer collection
LayerDescription.mqh CLayerDescription — the topology descriptor
BufferDouble.mqh CBufferDouble — host/device buffer
NeuronOCLConvPool.mqh CNeuronConvOCL, CNeuronPoolOCL
NeuronBatchNorm.mqh CNeuronBatchNormOCL — host-side batch norm, all tiers

Method bodies live in AI\Impl\, included at the bottom of Network.mqh after every declaration. This is a pure relocation — a body cannot run during compilation, and every declaration it could reference is already visible above it.

AI\Impl\ holds
NeuronBase.mqh CNeuronBase — init, forward, gradients, activation, persistence
NeuronConvPool.mqh CNeuronConv, CNeuronPool
NeuronLSTM.mqh CNeuronLSTM — gate layers, BPTT
Layer.mqh CLayer — element construction from a descriptor or a .nnw stream
NetBuild.mqh CNet lifecycle: topology construction, OpenCL/DirectML init
NetForward.mqh feedForward, backProp, getResults, logit adjustment
NetPersistence.mqh CNet::Save / CNet::Load — the .nnw format
NetWeights.mqh EMA blend, in-memory snapshot/restore, per-layer learning report
NeuronOCLBase.mqh CNeuronBaseOCL
NeuronOCLLSTM.mqh CNeuronLSTMOCL — sequence LSTM, fused kernels, BPTT caches

Compute tiers

Selected once at construction and logged; each layer runs entirely on one tier.

  1. OpenCLNetwork.cl, compiled from the #resource at startup.
  2. DirectMLWarriorDML.dll (D3D12 compute).
  3. CPU DLLWarriorCPU.dll, a threaded fallback. Thread count is fixed at CPU_THREADS_PER_NETWORK per net rather than a share of the machine; see the comment on that constant for why a machine-wide budget was wrong.
  4. Pure MQL5 — no DLL, no GPU. Single backtests run here so a Market build (which strips every #import) is still able to infer.

A Market build (WARRIOR_MARKET_BUILD) compiles tiers 2 and 3 out entirely.

Classes

  • CNet — owns the layer stack, the forward/backward passes and .nnw persistence. Note that a .nnw pins the architecture, not just the weights: Save writes each layer's activation and Load restores it, so a head activation changed in source only ever reaches a brand-new topology. EnforceOutputActivation() re-asserts it after every load.
  • CLayer / CArrayLayer — neuron containers; CreateElementScaled() is the single construction point for every neuron type, from a descriptor or from a file stream.
  • CNeuronBaseCNeuron / CNeuronPool / CNeuronConv / CNeuronLSTM — the pure-MQL5 family.
  • CNeuronBaseOCLCNeuronLSTMOCL / CNeuronConvOCL / CNeuronPoolOCL / CNeuronBatchNormOCL — the accelerated family. Despite the OCL suffix these also drive the DirectML and CPU-DLL tiers.

ENUM_ACTIVATION is NONE, TANH, SIGMOID, PRELU; ENUM_OPTIMIZATION is SGD, ADAM. NativeActivationCode() translates to the int code the backends share — and deliberately makes NONE fall through every backend switch.

Network.cl

22 kernels in four groups: dense (FeedForward, CaclOutputGradient, CaclHiddenGradient, UpdateWeightsMomentum/Adam), conv/pool (FeedForwardConv, FeedForwardProof, CalcHiddenGradientConv, CalcInputGradientProof, UpdateWeightsConv*), single-step LSTM (LSTM_Gates, LSTM_State, LSTM_*Gradient) and sequence LSTM (LSTM_SeqStep*, LSTM_UpdateWeights*).

Two invariants worth knowing before touching them:

  • MIN_ACTIVATION_DERIVATIVE (1.0e-3f) floors every LSTM gate derivative in both gradient kernels. It is already there — do not "add" it.
  • Kernel math is mirrored in DirectML\WarriorCPU.cpp and WarriorDML.cpp. A change to one is a change to all three, or the tiers silently diverge.

Usage

CNet *net = new CNet(topology);   // CArrayObj of CLayerDescription
net.feedForward(inputs);          // CArrayDouble
net.backProp(targets, weight);
net.getResults(out);
net.Save(path, ...); net.Load(path, ...);

The 3-output classification case is special-cased in backProp/backPropOCL: it computes a joint softmax + cross-entropy gradient across all three neurons rather than three independent per-neuron deltas.

Known gaps

  • No unit tests. Changes are validated by compile + a live era's dW/W line (CNet::LayerLearningReport), which is the only reliable check that a layer is actually receiving gradient in the assembled net.
  • No dedicated SoftMax layer; the head is Dense(SIGMOID) with the gradient short-circuited. See REFACTOR_NOTES.md §G for why, and for what would have to move together to change it.