forked from animatedread/Warrior_EA
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
130 lines
8.2 KiB
C++
130 lines
8.2 KiB
C++
//+------------------------------------------------------------------+
|
|
//| Warrior_EA |
|
|
//| Multithreaded CPU compute fallback - used when neither |
|
|
//| OpenCL nor the D3D12/DirectML tier are available (e.g. |
|
|
//| a VM with no GPU passthrough). Same buffer-handle model |
|
|
//| as WarriorDML.dll but full double precision throughout |
|
|
//| (no GPU float roundtrip) and work is spread across a |
|
|
//| configurable pool of worker threads instead of a device. |
|
|
//+------------------------------------------------------------------+
|
|
// Flat C ABI so MQL5 can #import this DLL directly. Function names and
|
|
// argument order/semantics mirror WarriorDML.h / AI\Network.cl 1:1 so the
|
|
// two backends are interchangeable behind CDirectMLMy in AI\Network.mqh.
|
|
#pragma once
|
|
|
|
#define WARRIORCPU_API extern "C" __declspec(dllexport)
|
|
|
|
// Every exported function (besides CPU_Init/CPU_GetHardwareConcurrency) takes a
|
|
// CpuHandle as its first argument - an opaque pointer to a heap-allocated,
|
|
// self-contained context (its own thread pool, buffer table and mutex) that
|
|
// CPU_Init() allocates and the caller (CDirectMLMy on the MQL5 side) is
|
|
// responsible for remembering and passing back on every subsequent call, then
|
|
// releasing via CPU_Shutdown(). No state is shared between contexts and this
|
|
// DLL keeps no global/static mutable state of its own, so it can be loaded any
|
|
// number of times and driven by any number of instances/threads in parallel -
|
|
// a fault or a wedged call against one context can never poison another
|
|
// context's calls, unlike a process-wide singleton would.
|
|
// A plain `long long` (not C++ `long`, which is only 32 bits on Windows) so it
|
|
// round-trips exactly through MQL5's 64-bit `long` on the #import side.
|
|
typedef long long CpuHandle;
|
|
|
|
// Lifecycle. CPU_Init(threads) always succeeds (returns a non-zero handle)
|
|
// since it needs no hardware - threads<=0 means "use
|
|
// std::thread::hardware_concurrency()". Returns 0 on failure.
|
|
WARRIORCPU_API CpuHandle __stdcall CPU_Init(int threads);
|
|
WARRIORCPU_API void __stdcall CPU_Shutdown(CpuHandle ctx);
|
|
WARRIORCPU_API int __stdcall CPU_GetLastError(CpuHandle ctx);
|
|
WARRIORCPU_API int __stdcall CPU_GetThreadCount(CpuHandle ctx);
|
|
// Stateless: true std::thread::hardware_concurrency(), independent of any
|
|
// context's pool size. Use this (not a CPU_Init(0)/GetThreadCount()/Shutdown()
|
|
// probe) to size a CPU_Init() request.
|
|
WARRIORCPU_API int __stdcall CPU_GetHardwareConcurrency();
|
|
|
|
// Buffer management. Handles are small non-negative integers scoped to ctx;
|
|
// -1 means failure. Buffers are plain double vectors owned by ctx; Write/Read
|
|
// just memcpy in/out, there is no upload/download step on CPU.
|
|
WARRIORCPU_API int __stdcall CPU_BufferCreate(CpuHandle ctx, int elementCount);
|
|
WARRIORCPU_API int __stdcall CPU_BufferWrite(CpuHandle ctx, int handle, const double *data, int count);
|
|
WARRIORCPU_API int __stdcall CPU_BufferRead(CpuHandle ctx, int handle, double *data, int count);
|
|
WARRIORCPU_API void __stdcall CPU_BufferFree(CpuHandle ctx, int handle);
|
|
|
|
// Compute kernels - argument order/semantics mirror AI\Network.cl and
|
|
// WarriorDML.h 1:1. activation: 0 = TANH, 1 = SIGMOID, 2 = PReLU (conv only).
|
|
WARRIORCPU_API int __stdcall CPU_FeedForward(CpuHandle ctx, int wHandle, int iHandle, int oHandle,
|
|
int inputs, int activation);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_CalcOutputGradient(CpuHandle ctx, int tHandle, int oHandle, int igHandle,
|
|
int activation, int count);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_CalcHiddenGradient(CpuHandle ctx, int wHandle, int gHandle, int oHandle, int igHandle,
|
|
int outputs, int activation, int count);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_UpdateWeightsMomentum(CpuHandle ctx, int wHandle, int gHandle, int iHandle, int dwHandle,
|
|
int inputs, double learningRate, double momentumRate, int neurons, int optimizer);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_UpdateWeightsAdam(CpuHandle ctx, int wHandle, int gHandle, int iHandle,
|
|
int mHandle, int vHandle,
|
|
int inputs, double lt, double b1, double b2, int neurons);
|
|
|
|
// Mini-batch gradient accumulation (2026-08-09 audit, F4) - mirrors AI\Network.cl's
|
|
// AccumulateWeightGrad / AccumulateWeightGradConv. These ADD one sample's per-weight gradient into an
|
|
// accumulator buffer; the optimizer step itself is applied host-side in MQL5 once per batch (see
|
|
// CNeuronBaseOCL::ApplyAccumulatedGradients), so there is deliberately no matching "apply" export.
|
|
WARRIORCPU_API int __stdcall CPU_AccumulateWeightGrad(CpuHandle ctx, int accHandle, int gHandle, int iHandle,
|
|
int inputs, int neurons);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_AccumulateWeightGradConv(CpuHandle ctx, int accHandle, int gHandle, int iHandle,
|
|
int inputs, int windowIn, int windowOut, int step);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_AccumulateBufferInto(CpuHandle ctx, int dstHandle, int srcHandle, int count);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_FeedForwardConv(CpuHandle ctx, int wHandle, int iHandle, int oHandle,
|
|
int inputs, int step, int windowIn, int windowOut, int activation, int positions);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_CalcHiddenGradientConv(CpuHandle ctx, int wHandle, int gHandle, int oHandle, int igHandle,
|
|
int outputs, int step, int windowIn, int windowOut, int activation, int inputCount);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_UpdateWeightsConvMomentum(CpuHandle ctx, int wHandle, int gHandle, int iHandle, int dwHandle,
|
|
int inputs, double learningRate, double momentumRate, int windowIn, int windowOut, int step, int optimizer);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_UpdateWeightsConvAdam(CpuHandle ctx, int wHandle, int gHandle, int iHandle,
|
|
int mHandle, int vHandle,
|
|
int inputs, double lt, double b1, double b2, int windowIn, int windowOut, int step);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMGates(CpuHandle ctx, int wHandle, int hiddenPrevHandle, int inputsHandle,
|
|
int concatenatedHandle, int hiddenSize, int inputSize);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMState(CpuHandle ctx, int concatenatedHandle, int memoryHandle, int hiddenPrevHandle,
|
|
int hiddenCacheHandle, int outputHandle, int hiddenSize);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMGateGradient(CpuHandle ctx, int gradientHandle, int memoryHandle, int concatenatedHandle,
|
|
int concatenatedGradientHandle, int hiddenSize);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMWeightsGradient(CpuHandle ctx, int concatenatedGradientHandle, int hiddenCacheHandle,
|
|
int inputsHandle, int weightsGradientHandle, int hiddenSize, int inputSize);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMInputsGradient(CpuHandle ctx, int concatenatedGradientHandle, int wHandle,
|
|
int inputsGradientHandle, int hiddenSize, int inputSize);
|
|
|
|
// Fused unrolled sequence LSTM - see the block comment above the definitions in WarriorCPU.cpp.
|
|
// These replace the per-step entry points above for sequence models: the per-step ones treat the whole
|
|
// input as ONE timestep, and CPU_LSTMGateGradient cannot accept dc from the following step, so real
|
|
// backpropagation-through-time cannot be assembled from them.
|
|
WARRIORCPU_API int __stdcall CPU_LSTMSeqForward(CpuHandle ctx, int wHandle, int inputsHandle,
|
|
int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outputHandle,
|
|
int hiddenSize, int stepInputs, int steps);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMSeqBackward(CpuHandle ctx, int wHandle, int inputsHandle,
|
|
int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outGradientHandle,
|
|
int weightsGradientHandle, int inputsGradientHandle, int hiddenSize, int stepInputs, int steps);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMUpdateWeightsAdam(CpuHandle ctx, int wHandle, int weightsGradientHandle,
|
|
int mHandle, int vHandle, double l, double b1, double b2, int total);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_LSTMUpdateWeightsMomentum(CpuHandle ctx, int wHandle, int weightsGradientHandle,
|
|
int dwHandle, double learningRate, double momentumRate, int total, int optimizer);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_FeedForwardProof(CpuHandle ctx, int iHandle, int oHandle, int inputs, int window, int step, int outputs);
|
|
|
|
WARRIORCPU_API int __stdcall CPU_CalcInputGradientProof(CpuHandle ctx, int iHandle, int gHandle, int oHandle, int igHandle,
|
|
int outputs, int window, int step, int inputs);
|