Commit graph Warrior_EA/Expert/ExpertSignalAIBase.mqh
Author SHA1 Message Date
AnimateDread
80a53d5b04 feat: cap class sample weight and increase training time budget
- Added MAX_CLASS_SAMPLE_WEIGHT (3.0) to cap the sample weight multiplier, preventing anti-correlated Buy/Sell recall whipsaw observed when per-era ratio overwrites previous separability.
- Added MIN_OOS_CLASS_SAMPLES_FOR_GATE (10) to require a minimum number of true OOS samples before trusting class recall, avoiding degenerate false-convergence cases.
- Increased TRAIN_TIME_BUDGET_MS from 80 to 500 to improve throughput under MT5's custom event dispatch model (which determines actual pacing); trades UI responsiveness (~500ms lag) for roughly 6× more bars processed per event.
2026-07-17 09:11:42 -04:00
AnimateDread
ed2285000b feat: widen label cache to include neighborhood around pivot bars
The exact-bar-only label scheme caused OOS Buy/Sell recall stuck at 0-3% because the feature window cannot distinguish a pivot bar from its near-identical neighbor. By extending each confirmed pivot into a +-LABEL_WINDOW_BARS neighborhood (default 2), the network is trained to recognize bars near a real reversal rather than only the exact tick. The spread uses a frozen snapshot of the original scan to prevent cascading propagation.
2026-07-16 22:29:49 -04:00
AnimateDread
17abf513ca feat: bias output layer toward the real class prior to fix era-0 cold-start noise
A freshly-initialized network's weights are plain random init with no
informed prior, so its argmax is close to uniform noise across the 3
classes - on this project's typically heavily Neutral-skewed label
distribution, that means era 0 fires far more spurious Buy/Sell calls
on the very first (oldest) bars than the true base rate warrants,
until enough backProp steps correct it.

The label-cache prebuild already computes the real upfront Buy/Sell/
Neutral tally before era 0 starts. Added CNet::SeedOutputLayerBias()
(AI/Network.mqh) to overwrite just the output layer's bias term (the
per-input weights stay randomly initialized and still carry the real
learning signal) with a fixed +-3.0 push toward whichever class
actually dominates - reaches one layer back from the output layer,
since that's where the weight block producing it is stored. Wired in
at the end of AdvanceLabelCachePrebuild(), fresh-network-only.
2026-07-16 20:37:36 -04:00
AnimateDread
721f2970f2 feat: normalize CumulativeDelta feature, add explicit bullish/bearish flag
ADCumulativeDelta's CumulativeDelta buffer now outputs
cumulativeDelta/sumVolume clamped +/-2 instead of a raw, unbounded,
tick-volume-scale running sum - also fixes the indicator's own
declared -2..2 min/max property, which the raw sum silently violated.
Re-added to the feature set now that it's properly scaled, as a
distinct order-flow-imbalance signal from Pressure (same term plus
Initiative/Absorption adjustments).

Added an explicit +1/-1/0 bullish/bearish/doji flag alongside the
ATR-normalized price features, so the network gets candle direction
as a clean standalone signal instead of having to disentangle it from
(close-open)/atr's combined direction+magnitude encoding.
2026-07-16 19:55:51 -04:00
AnimateDread
fbe4821df2 fix: replace class-balance oversample replay with loss weighting
The training log showed the model had genuinely collapsed to always-predict-
Neutral: 100+ consecutive eras with Buy/Sell OOS recall flat at 0% and IS
error frozen exactly at 0.14, not a "still early" transient. Root cause is
the 33:1 Buy/Sell-vs-Neutral label imbalance combined with the oversample
replay cap - capped at 3x specifically because more identical back-to-back
backProp() calls fed Adam highly-correlated gradients and caused runaway
momentum (a past incident: OOS accuracy diving from 90%+ to single digits
within ~20 eras). That cap meant minority classes never got enough gradient
influence to matter once the model settled into all-Neutral.

Replaced the N-times replay with a single backProp() call per example, with
its output-layer gradient scaled by inverse class frequency (maxCount/
trueCount from the previous era's true label distribution, uncapped - the
existing MAX_WEIGHT_DELTA per-step clip already bounds how far any single
step can move a weight regardless of gradient magnitude, so there's no
repeated-gradient momentum risk left to cap against).

AI/Network.mqh: CNet::backProp()/backPropOCL() take a new optional
sampleWeight parameter (default 1.0, so every other caller is unaffected).
For the OCL/DirectML path, since WarriorCPU.dll/WarriorDML.dll/Network.cl
have no notion of per-sample weighting, the raw gradient computed by the
native CalcOutputGradient call is read back into MQL5, scaled, and written
back via a new CNeuronBaseOCL::setGradient() before the hidden layers read
it - no changes needed to any of the 3 compute backends themselves.

Also fixed: a run that "converged" at era 63 only because that specific
era's small OOS sample happened to contain zero true Buy/Sell examples
(recall shows n/a and auto-passes the gate when a class is absent from an
era's sample) - the model had already fully collapsed several eras earlier;
this was a lucky/unlucky sampling fluke, not real convergence. Not fixed in
this commit (separate, narrower issue - the gate's n/a auto-pass exists to
avoid deadlocking on a genuinely rare class, and distinguishing that from a
collapsed model needs its own follow-up).

Needs a fresh retrain like the prior structural fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:04:56 -04:00
AnimateDread
4f0108366f fix: remove undocumented input noise and unbounded/duplicate features
AI/Network.mqh: CNet::feedForward() was adding sin(i)/cos(i) (alternating by
index parity) directly onto every single input value, on both the CPU and
GPU/DirectML paths - an undocumented, uncontrolled +/-1 offset baked into
already-normalized features that mostly live in a much smaller range. No
comment anywhere explained it, unlike the rest of this codebase; removed.

Expert/ExpertSignalAIBase.mqh, data normalization cleanup:
- Volume relative-change feature is now clamped to +/-5 - unlike the
  ATR-normalized price features, it had no ceiling, so a thin previous bar
  (e.g. 1 tick) followed by a normal one could feed the network an outlier
  input an order of magnitude past every other feature's range.
- Dropped ADCumulativeDelta's raw CumulativeDelta buffer (1) from the feature
  set - it's an unbounded, tick-volume-scale running sum, unlike every sibling
  buffer in that same indicator (all explicitly clamped +/-2). Pressure
  (buffer 0) is this same signal already normalized, so nothing is lost.
- Dropped ADWyckoffEventStream's EventPhase buffer (1) - confirmed via source
  it's a byte-for-byte duplicate of EventCode (same underlying variable), not
  a distinct phase reading; StructuralPhase (buffer 5) is the real phase
  signal and stays.

m_neuronsCount updated accordingly (topology changes, needs a fresh retrain
same as the earlier window-offset fix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 18:09:06 -04:00
AnimateDread
10f52084a8 fix: training progress Comment() showed the true label, not the prediction
Moving the per-bar window/feedForward step before the label-check/backprop
step (previous commit) meant the label-check block now runs LAST each
iteration, clearing TempData and refilling it with the smoothed target label
(0.1/0.9) for backProp - so by the time the next iteration's Comment() call
read TempData[0..2] to display "Buy/Sell/Neutral Neuron", it was showing the
PREVIOUS bar's true label, not the network's actual prediction. Since ~94% of
bars are Neutral-labeled, this mostly looked like "Neutral stuck near 0.9-1,
Buy/Sell stuck near 0.1" regardless of what the model was really outputting.

Snapshot the softmax probabilities into local vars right after they're
computed (before the label-check block can clobber TempData), and moved the
whole Comment() build to run right after that snapshot instead of at the top
of the loop, so it now shows the CURRENT bar's own genuine prediction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 17:46:27 -04:00
AnimateDread
819066819d fix: feature window never included the most recent bars near the prediction target
RefreshLatestSignal(), AdvanceOosSimulationChunk(), and Train()'s per-bar window
build all constructed the historyBars-wide feature window starting historyBars
bars past the target bar (r = i + historyBars) instead of ending at it (r = i) -
so the model was always blind to the most recent historyBars bars of price
action right before the bar it was being asked to call, both in training and
live inference. This likely explains why Buy calls fire but land poorly timed
and Sell almost never clears its recall floor.

Also reordered Train()'s main loop so each bar's own freshly-built window/
prediction is what gets checked against that bar's label immediately (matching
AdvanceOosSimulationChunk's existing predict-then-learn pattern), instead of
training against the previous iteration's stale prediction.

Also added a Print() when training genuinely converges (m_objectiveMet &&
m_oosStable) - this was previously silent, only visible via the persisted
TrainingComplete flag.

Existing trained weights encode the old, shifted task and need a full retrain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 15:23:14 -04:00
AnimateDread
e39cfa56c1 fix: stop halving the WarriorCPU.dll thread pool for phantom concurrency
TargetCPULoad=max was only ever getting ~half the cores (e.g. 6 of 12)
even outside HYBRID mode, because every CNet (including the live net's
own EMA shadow net, which every signal has) counted as a "peer" that
divided the requested CPU budget.

That division was solving a problem that can't actually happen: MQL5
gives one chart's EA a single execution thread, and every WarriorCPU.dll
entry point blocks that thread until its own ParallelFor() completes,
so only one CNet's worker pool is ever actively computing regardless of
how many are alive (live, shadow, or - under HYBRID - PAI/CONV/LSTM's
own pairs). The idle ones sit blocked on a condvar at ~0% CPU. Dividing
the budget across "peers" that never contend just capped whichever pool
happened to be running at a fraction of the cores actually available.

Removes the now-pointless g_netPeerCount/EffectiveCpuLoadPercent()/
PeerNetworkCount() machinery entirely; SetCpuLoadPercent() now just
takes TargetCPULoad directly.
2026-07-16 13:51:28 -04:00
AnimateDread
feb82a61aa feat: log per-class OOS recall in the periodic training progress line
Recall (directionalRecallOK) was only ever printed on a regression
event, so once OOS accuracy alone cleared the target there was no way
to tell from the Journal whether recall was the thing still blocking
m_objectiveMet. Now shown on every ~5s progress line.
2026-07-16 12:25:05 -04:00
AnimateDread
78ef4c14b9 fix: classification training was structurally unable to converge
Two issues compounded to keep the network oscillating for 350+ eras
without ever reaching m_objectiveMet, despite good OOS accuracy:

- CNet::backPropOCL() zeroed the diagnostic error contribution from
  any output neuron whose target was exactly 0, unlike backProp()'s
  CPU-fallback path. For the one-hot 3-class classification head this
  meant dError only ever reflected the true class's own calibration,
  never whether the other two classes were correctly suppressed -
  purely a reporting bug, the real gradient (CPU_CalcOutputGradient
  in WarriorCPU.cpp) was already correct and unaffected.
- Train()'s convergence gate required dError < 0.1 unconditionally.
  That RMS-error floor is reachable for the single-neuron regression
  head, but for 3 one-hot outputs it demands near-perfect confident
  calibration on every bar - unreachable under normal market label
  noise, and redundant with the classification-native gates already
  in place (dOosForecast >= target, per-class directionalRecallOK).
  Now skipped when m_outputNeuronsCount == 3.
2026-07-16 08:55:54 -04:00
AnimateDread
eee45526a5 fix: remove redundant Expert.Deinit() in OnInit(); correct ADZigZag buffers
- Remove manual Expert.Deinit() calls from all failure branches in OnInit() because MQL5 automatically calls OnDeinit(REASON_INITFAILED) when returning non-INIT_SUCCEEDED, preventing double deinitialization and invalid pointer access in OnDeinit().
- Change ADZigZag buffer count from 1 to 3 to match the indicator's #property indicator_buffers (3 buffers), accounting for internal INDICATOR_CALCULATIONS buffers, consistent with other AD Wyckoff indicators.
2026-07-16 01:12:37 -04:00
AnimateDread
0bfc441139 feat: real-ZigZag training labels, HYBRID CPU-oversubscription fix, input cleanup
- Replace the hand-rolled fractal/deviation-%/ATR-trend-context ZigZag
  approximation with the actual MQL5 ZigZag indicator (rebranded as
  CustomIndicators/ADZigZag.mq5, logic untouched) as the training label
  source; bump the settle/confirmation window from 20 to 100 bars so a
  proper leg can form before being trusted, and drop the now-dead
  ZigZagDeviationPct/MinTrendATRMultiple/TrendContextBars inputs.
- Rebrand the stock Volumes indicator the same way (ADVolume.mq5).
- Fix HYBRID mode (AIType=HYBRID) oversubscribing the CPU fallback tier:
  every concurrent CNet instance was independently sizing its worker
  pool off the same global TargetCPULoad input. g_netPeerCount/
  PeerNetworkCount() now split it across however many CNet instances
  (live+shadow x active signals) are actually sharing the CPU.
- Fix NEURONS_REDUCTION_FACTOR's confusing retention-vs-reduction
  semantics so RF_70 means an actual 70% reduction; default to 4 hidden
  layers / RF_70.
- Migrate remaining free-form training/indicator inputs to enums for UI
  consistency; default news filter lookback to 1h, disable every-tick.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 00:56:33 -04:00
AnimateDread
8c66a0fb89 fix(cl): stabilize network training with tighter limits, weight decay, and sign-agreement gate
- Tighten MAX_WEIGHT from 1.0e6 to 100.0 to prevent unbounded weight growth.
- Add MIN_ACTIVATION_DERIVATIVE (1e-4) to avoid zero gradients for saturated units.
- Introduce WEIGHT_DECAY (0.01) to decouple decay from Adam updates.
- Add MAX_WEIGHT_DELTA (0.1) to clamp per-step Adam updates and prevent overshoot.
- Apply sign-agreement gate on weight updates in Adam kernel to only apply steps aligned with current gradient direction.
- Fix tanh/sigmoid derivative calculations to use the new floor instead of hard-coded edge-case values.
2026-07-15 21:47:37 -04:00
AnimateDread
c05d9d1a1f fix: correct OpenCL gradient and weight update for classification layers
- In CaclOutputGradient kernel:
  - Case 1 (sigmoid classification): removed erroneous multiplication by out*(1-out) which dampened gradients – the binary cross-entropy loss already cancels the sigmoid derivative, so direct (target-out) is correct.
  - Added default case for NONE activation (softmax classification) to compute plain error (target-out); previously unhandled, resulting in zero gradients that froze the entire network when OpenCL was active.
- In UpdateWeightsMomentum and UpdateWeightsAdam kernels: added clamp to MAX_WEIGHT when updating weights to prevent gradient spikes from producing ±Infinity and subsequent NaN propagation through dense layers (e.g., classification output head).
2026-07-15 21:47:09 -04:00
AnimateDread
ac88fc1ac5 fix(Expert/ExpertSignalAIBase): add feature cache and fix oversampling imbalance
- Introduce per-bar feature cache to avoid redundant recomputation of input vectors during training.
- Rename EnsureLabelCacheCapacity to EnsureBarCachesCapacity to reflect management of both label and feature caches.
- Fix oversampling logic to maintain balanced representation among minority classes, replacing independent 5x caps that caused relative bias.
2026-07-14 22:49:14 -04:00
AnimateDread
068495b3d7 fix: clean up EA init/deinit lifecycle and close a DirectML mutex-poisoning hang
Warrior_EA.mq5: clear Comment() and destroy the control panel before
Expert.Deinit()'s object-purge cascade runs out from under it; stop
re-registering the same signal filters on every DB retry (was causing a
double-delete of the same pointer on shutdown).

DirectML/WarriorCPU.cpp: bound the worker-thread join in ThreadPool::Stop()
instead of blocking forever - CPU_Shutdown() held g_mutex across an unbounded
join, so a watchdog-killed calling thread could leave it locked forever,
poisoning every future call into the DLL (matches reports of the EA getting
stuck on "initializing" after being removed and re-added to a chart).

Also includes prior era-0 label-cache prebuild and pullback/reversal
label-quality work in AI/Network.mqh, Expert/ExpertSignalAIBase.mqh, and
Variables/Inputs.mqh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 22:36:27 -04:00
AnimateDread
ad154a4897 Merge branch 'main' of https://forge.mql5.io/animatedread/Warrior_EA 2026-07-14 18:05:43 -04:00
AnimateDread
1204b9df97 feat: add percentage-based CPU load input for fallback tier
Replace the absolute thread count input (CpuDllThreads) with a percentage-based CPU_LOAD_PRESET enum (TargetCPULoad). This allows users to specify a percentage of detected cores to use when the CPU DLL fallback is active, improving flexibility and preventing issues when multiple instances share the same CPU DLL pool. Also adds CPU_GetHardwareConcurrency() for accurate core detection.
2026-07-14 18:04:48 -04:00
AnimateDread
5a3310b5cc 2026-07-13 19:19:42 -04:00
AnimateDread
9464e52784 feat: embed compute DLLs as resources and extend Save/Load for training state
Add #resource directives for WarriorDML.dll and WarriorCPU.dll, and ExtractComputeDlls() to extract them on first run, enabling MQL Cloud Protector builds to ship DLLs without manual copy. Extend CNet::Save and Load with trainingComplete and indicatorParams arrays to persist AutoTune indicator param values.
2026-07-13 15:59:35 -04:00
AnimateDread
f2a30aa9cf fix: avoid TANH gradient vanishing by omitting derivative in output error and add era field to serialization
The TANH activation's derivative (1-out^2) approaches zero when the output nears ±1, stalling training exactly where convergence to extreme values (e.g., buy/sell signals) is needed. Removing the multiplication by this derivative in the output gradient calculation (both CPU OpenCL and MQL4 paths) prevents this saturation, analogous to using cross-entropy with sigmoid.

Additionally, extend `Save()` and `Load()` to include a new `era` field, enabling tracking of training generations across sessions.
2026-07-13 08:23:30 -04:00
AnimateDread
74c7395127 feat: add max-pooling and convolution OpenCL kernels, clean up barrier and signal code
- Define MAX_WEIGHT constant (1.0e6) for weight limits in clusters
- Remove redundant barrier from FeedForward kernel (prevents sync issues)
- Port FeedForwardProof and CalcInputGradientProof kernels for max-pooling (no weights, sliding max)
- Port FeedForwardConv kernel for convolution layers (shared weights, multiple output channels)
- Remove unused code and refactor signal condition logic (CSignalPAI)
2026-07-13 03:23:39 -04:00
super.admin
0a527b0cf9 convert 2025-05-30 16:35:54 +02:00