Commit graph

492 commits

Author SHA1 Message Date
AnimateDread
31e16e9487 feat(tuner+altdata): tuner optimizes RANGE not direction; copy-paste whitelist UX on 4014
- MI_TUNE_TARGET = MI_TARGET_EXC_RANGE: the coordinate sweep scored
  candidates against the barrier label - measured noise - so it climbed a
  flat landscape and the gate rightly rejected every winner. It now
  selects indicator settings for MI vs realised RANGE (4x null, positive
  control), the channel the excursion head consumes these features for.
  Winner gate re-tests on the same target. Barrier-label report unchanged.
- AltDataFetch 4014 handling: Alert popup + once-per-session walkthrough
  with the two whitelist URLs on their own journal lines (copy-paste
  ready); hourly-backoff retry instead of a permanent latch, so the
  whitelist fix takes effect without re-attaching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 14:44:11 -04:00
AnimateDread
51093697e2 test(altdata): EIA x WTI screen - NOTHING clears the family bar, EIA stays research-side
12 features (5 EIA petroleum + 3 COT managed-money + VIX/USD + controls)
vs forward 5-bar range and direction on 1,789 D1 bars resampled from the
decoded XTIUSD M1 file, 499 circular-shift perms. No feature clears the
family-wise bar on either target; every EIA fundamental is null even
marginally (best p=0.13). Sample is short (~7y) so a weak effect is not
excluded - but per the gate, no EIA feature ships. The EIA key stays in
keys.txt for future use (longer history / recorded surprise-vs-consensus).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 14:04:37 -04:00
AnimateDread
8657c4fa12 feat(altdata): EA-side self-sufficient alt-data collection (WebRequest + OnTimer)
System\AltDataFetch.mqh: the EA backfills missing alt-data history at
attach and keeps appending forward while deployed - online learning never
depends on an external process. CFTC Socrata API (no key, 2006->now, one
GET per symbol; ES name variants verified, max-OI dedupe) + FRED (VIXCLS/
DTWEXBGS, key from AltData\keys.txt). Identical publication stamps and
fixed a-priori transforms as research/altdata/export.py; rebuilds the
same {SYM}_D1.csv files, so Python and EA interoperate on one format.
OnTimer hook (30-min staleness check, in-memory compares when current;
never in tester - cache files serve there) + AltDataReload() on signals.
Classic-signal removal CANCELLED per user (vote experiment later).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:59:03 -04:00
AnimateDread
8ce635ce70 feat(altdata): external feature block wired into the NN feature window
- System\AltData.mqh: CAltDataPanel - publication-stamped CSV panel
  (Common\Files\Warrior_EA\AltData\{SYM}_{TF}.csv), as-of lookup by bar
  open, 0-fill degradation (mirrors cross-asset), hourly live refresh
- Topology: width block AFTER the .cfg name-list pin is pre-read
  (ReadAltDataPinFromCfg) so a grown export can never mismatch a resumed
  model's width or shift its slots
- Persistence: alt pin appended to the .cfg (append-and-length-guard
  convention), adopt-don't-compare on load
- Features: emit block after Wyckoff SBI; EnsureFresh probe in
  BuildFeatureWindow (never fires in tester)
- export.py: fixed a-priori scale constants (never data-fitted)

Widths change SP500 +4 / USDJPY +3 / XAUUSD +1 (fingerprint re-keys ->
fresh models on redeploy); EURUSD exports nothing and resumes unchanged.
Compiles 0 errors / 0 warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:39:00 -04:00
AnimateDread
6d50e63370 feat(altdata): conditional (incremental) MI + direction-channel screen
MI|vol column = I(X; target | trailing-range tercile), same circular-shift
null. Range target 499 perms: SP500 vix_chg5 survives conditioning at 0.031
(3x trailing range's own within-tercile residual); VIX LEVEL emerges
conditionally (variance-risk-premium structure); USDJPY COT family survives.
Direction target: SP500 vol/VIX-chg clear marginally (equity leverage
effect) but drop to p~0.05-0.06 conditional = redundant with price vol;
USDJPY/XAUUSD/EURUSD direction null across all alt features.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:57:27 -04:00
AnimateDread
4a56d8da48 feat(altdata): FRED/EIA collectors live + first MI screen - COT clears on USDJPY, VIX-change on SP500
- fred.py: ALFRED output_type=4 first prints, chunked realtime windows
  (2000-vintage cap), unrevised-series fallback (published=observed+1d);
  NFCI excluded from features (revised, no vintage archive)
- eia.py: 4 weekly petroleum series on disk (1982->now)
- screen.py: as-of joined alt features vs forward 5-bar range/ATR on D1,
  3x3 MI, circular-shift null, family-wise max bar, +/- controls

First readings (199 perms): SP500 vix_chg5 MI 0.047 (1.5x the positive
control) + usd_chg5 clear family bar; USDJPY 4 COT positioning features
clear family bar BEATING the positive control; XAUUSD vix_chg5 tops control
but sub-family-bar; EURUSD positive control FAILS -> table void per the
excursion-target rule, needs investigation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:45:21 -04:00
AnimateDread
6d1ecb71ce feat(altdata): alternative-data collector package - COT shipped, FRED/EIA ready
Private-use pivot (marketplace dropped): DLL/Python/WebRequest now allowed.
- altdata/cot.py: CFTC COT, no key, 2010->now on disk for all 9 symbols
  (TFF: ES/VIX/BTC/EUR/JPY/CAD/GBP; Disagg: GC/CL); publication-lag stamping
  (Tuesday report -> Saturday 00:00 UTC availability)
- altdata/fred.py: ALFRED first-print vintages (needs free key)
- altdata/eia.py: weekly petroleum status (needs free key)
- DESIGN.md: source adjudication (corrections to the LLM source list),
  vintage + family-wise rules, EA file contract, staged EA-side plan

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:29:47 -04:00
AnimateDread
430cdbe650 feat(ai): conditional barrier geometry for the fractal target - MFE/MAE measured leg-by-leg at labeled bars
The derived stop/target were quantiles of EVERY bar''s excursions over a
fixed horizon - q75 adverse gave a 2.6-3.5*ATR stop against a ~1.7*ATR
target (user: "looks limiting"). That pooled measurement was correct
when direction was dead (any subset of bars had the same distribution)
and is provably mis-sized now that the gate certifies the label carries
information: the bars the model trades are the labeled bars, and their
excursions differ from the pool.

FractalDirectionLabel now records, for every Buy/Sell-labeled IS bar
during the prebuild, the favourable and adverse travel in ATR units
over exactly the LEG the label points at - entry close through the next
fractal extreme (user request: "from a fractal to the next for maximum
accuracy"). DeriveBarrierGeometry reads the same q75-adverse/q50-
favourable quantiles off that conditional sample instead of the pool,
with a logged fallback to pooled when fewer than the minimum legs
exist. Quantiles kept over averages deliberately: a mean MFE is
dominated by runaway legs and would set an unreachable target.

No circularity: the fractal label does not depend on SL/TP (the barrier
label does - this path must never feed it). Recording stops the moment
geometry is derived and pinned, so pass 2 relabels and later bars
cannot silently re-shape a certified pair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 19:00:40 -04:00
AnimateDread
7dff532c70 fix(ensemble): shared MI diagnostics + divided chunk budget - warm-up and panel responsiveness
Two user-reported ensemble regressions, one cause each:

- "getting ready is very long": every member ran the full MI diagnostic
  suite (headline MI, positive control, alignment, lag profile,
  geometry scan + winner test - ~200 permuted draws per line) on
  IDENTICAL features and labels, reporting the same numbers four times.
  First member runs it, the rest adopt with one log line. Documented
  caveat: if the geometry scan ever ADOPTS a winner under its gate
  (it never has), the adoption becomes donor-only and the gate must be
  revisited.
- "panel not responsive": four members chunks queue back-to-back on the
  one chart thread - 4 x 120ms = 480ms worst-case click latency, the
  exact regime the 200ms note in Training.mqh already documents as
  broken. Ensemble members now use a 30ms chunk budget, restoring solo
  UI latency at slightly higher dispatch overhead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 17:37:10 -04:00
AnimateDread
a734b91e80 feat(ui): one aggregated status panel for the AI_HYBRID ensemble
All four ensemble members previously wrote their full multi-line panels
to the SAME global label objects - an ensemble chart would flicker
between four stacked panels covering the chart side (user request:
aggregate). Every AI-side SetStatusLabel call site now routes through
CExpertSignalAIBase::PublishStatus - solo charts draw the full panel
exactly as before; an ensemble member claims a slot and contributes
only its HEADLINE to one combined block ("HYBRID ensemble - N models",
then one line per model; the live line leads with the model current
signal). The combined render skips unchanged text and enforces its own
minimum redraw interval so four publishers cannot multiply
ChartRedraw() cost.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:54:43 -04:00
AnimateDread
609be10391 feat(ai): AI_HYBRID = ensemble preset (all NNs, one chart); conv+recurrent renamed AI_CONVLSTM
The user is right that no special combination logic is needed: the AI
signals are ordinary voting filters, and the aggregate already has
union semantics - abstaining filters do not dilute the average, so an
ensemble chart trades whenever ANY deployed member clears the vote
threshold and disagreeing members net out. What the ensemble preset
actually adds:

- AI_CHOICE value 4 renamed AI_CONVLSTM (the name says the front-end);
  enum VALUES stable, CSignalHYBRID class and State\HYBRID\ folder kept,
  so saved configs and trained models keep their identity.
- New AI_HYBRID = 6: enables PAI+CONV+LSTM+CONVLSTM together on one
  chart - replaces four separate charts of the same symbol. Each member
  trains and self-gates independently; only certified members ever vote.
- |ENS1 fingerprint token on every member, so an ensemble member's
  weight files can never collide with a solo model of identical
  settings on another chart of the same symbol (the duplicate-chart
  guard would otherwise correctly fight over one .nnw).
- Private default AIType = AI_HYBRID: one D1 drop now yields every
  topology's gate verdict for that symbol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:50:36 -04:00
AnimateDread
7cc6e35adc fix(exits): hold-to-barrier policy for fractal-target charts - live trades now match the certificate
The first-ever family-wise gate pass (SP500 D1 PAI, +10.4pp, p=0.0081)
certifies a win rate measured on HOLD-TO-RESOLUTION outcomes: entry,
then the measured SL or TP decides. Live, three vote-driven exit routes
could close earlier - the averaged-vote close, the AI early-exit route
(both in CheckClosePosition), and CheckReverse - and the fractal
target's vote flips at swing-marker cadence (~3-5 bars), far inside the
barrier's typical travel time (median 7-8 D1 bars to target). The user
observed exactly this: an opposite arrow near an entry, trade cut,
price kept going.

On a fractal-target chart with a live direction model, all three routes
are now suppressed (m_holdToBarrier, set in InitializeSignal, loudly
logged): positions run to their broker SL/TP. Risk guards and trailing
are deliberately untouched - account protection is not signal opinion.
Barrier-target models keep the vote exits: their label is the vote's
own horizon, so for them the routes are semantically consistent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:18:11 -04:00
AnimateDread
f55dbe995c feat(research): drift null for the first-ever family-wise gate pass (SP500 D1 fractal PAI)
2026-08-15, ~6 minutes after attach: PAI-8fea (fractal target, 37
features, D1) converged at era 299 and the plateau deploy CLEARED the
family-wise gate for the first time in project history: dir-precision
73.1% vs 63% break-even, +10.4pp on 350 test calls = 4.04 sigma,
p_family = 0.0081.

This script asks the first two hostile questions offline:
- DRIFT: always-long at the same 2.64/1.66 geometry scores 64.1% on the
  last 15% of D1 history (66.7% on 30%) - drift alone clears BE by
  ~1-3pp, but the model is +9pp above ALWAYS-LONG, so the pass is
  selection, not drift.
- SWAP EXPOSURE: median 7-8 bars to the long target = ~10 nights of
  financing ~ 0.15-0.2% notional vs a ~1.7% target -> a ~1-1.5pp BE
  haircut against a +10.4pp margin. Survives.

Remaining before belief: replication on other D1 symbols, and closing
the live-semantics gap (certified wins assume hold-to-barrier; live
exit paths can cut on vote flips).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:09:08 -04:00
AnimateDread
61a8c42a9c feat(ai): TrainingTarget input - fractal-direction label for the direction models
User direction (2026-08-15): back to predicting swing turns, D1 charts,
fractals over ZigZag pivots (their call - balances classes, matches the
reference library target, and a 5-bar fractal confirms 2 bars after its
extreme so labels resolve nearly to the present with no repaint embargo).

- TRAINING_TARGET enum + TrainingTarget input: TARGET_BARRIER (Market
  default - existing models keep their meaning and fingerprints) or
  TARGET_FRACTAL (private default).
- FractalDirectionLabel (Labels.mqh): per-bar 3-class label = direction
  from the bar close to the next confirmed strict 5-bar fractal extreme,
  costs charged in the same bid-series convention as the barrier label,
  Neutral when the move cannot clear max(2 spreads, 0.10 ATR) or on an
  outside bar (both-extreme bars are unorderable within OHLC).
- The barrier walk still runs in full: measured SL/TP geometry, the
  expectancy scan, excursion caches and the era gate all keep scoring
  what a trade at the EA's own stop/target actually collected - only the
  TRAINING label changes. NOT the pre-b4a704d "is this bar the pivot"
  form; that target's 31:1 imbalance stays retired.
- Fingerprint token |TGT:FRA1 so switching targets trains a separate
  model; AI_META unaffected (guarded setter).
- Private defaults: AIType back to AI_HYBRID (direction topology needed)
  + TrainingTarget=TARGET_FRACTAL = drop-on-D1-chart workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 04:44:10 -04:00
AnimateDread
4e00955463 feat(research): pooled fractal-pivot direction NN - the last clean look at direction, gate FAILED 4/4
User request: NNs predicting pivots (fractals for label density). Run on
the offline stack that demonstrably CAN learn (+2.6pp XAUUSD meta), free
of every historical in-EA training bug: pooled 4-symbol training,
per-symbol norm, scale-free causal features, target = side of current
price the next confirmed Bill Williams fractal lands on, real M1
ask/bid fills, threshold fitted on calib only, pre-registered 2-sigma
net-expectancy gate.

Result: train CE 0.682 (a whisper below the 0.693 coin), and on test no
symbol passes - EURUSD/USDJPY net zero, XAUUSD gross +0.22 pts vs a
larger spread (net -0.30), SP500 net +0.05 +/- 0.45. The gross-positive
tails are index drift plus sub-spread micro-reversion - the tick-flow
decay shape at swing scale. Seventh independent measurement of the same
fact: entry-time direction information is not in these features.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 18:33:11 -04:00
AnimateDread
c85f6a3839 feat(research): stock-ZigZag live replay - the repaint measurement that answers "trade the true swings"
Faithful Python port of ADZigZag (stock MetaQuotes ZigZag 12/5/3,
verbatim rebrand) including the incremental prev_calculated branch, so
the indicator can be replayed bar by bar exactly as it draws live.

SP500 H1, 74,599 bars, fills at real M1 ask/bid:
- FINAL swings: 4,658 legs, mean 50.8 pts = 95 spreads. Perfect
  foresight +50.2 pts/leg. The user premise (swings dwarf spread) is
  fully confirmed.
- LIVE: 81% of drawn newest-pivots later repaint away entirely
  (18,805 of 23,299). Holding the drawn direction at every bar close
  grosses +0.38 pts/trade (t=0.8, zero cost charged) out of the
  50.8-pt average swing - 0.7% of the line the chart ends up showing.
- LONG +1.66 gross / SHORT -0.90 = the index drift, nothing else;
  long net +1.17 pts / 13-bar hold = ~1.5 bp, under one night financing.

The spread subtracts 0.49 pts of a swing that hands over 0.38: cost was
never the obstacle - pivot knowledge is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 18:12:11 -04:00
AnimateDread
9c6c81a706 feat(research): pivot-to-pivot swing expectancy - the spread-vs-swings question measured
User claim: swings dwarf the spread, so cost cannot be what blocks swing
trading at 1:2/1:3 RR. Measured on the validated M1 bid/ask book, SP500
H1, ATR-scaled causal ZigZag at 4 reversal thresholds:

- The premise is CONFIRMED: median swing 36-79 spreads, mean up to 110.
  Perfect-foresight expectancy +28 to +59 pts/leg.
- The conclusion does not follow: trading every confirmed leg (enter on
  the ZigZag confirmation close, real ask/bid fills, exit on the next
  confirmation) grosses -0.2 to -0.5 pts/leg AT ZERO COST, on 3,681 to
  13,871 legs. The confirmation retracement - the event that DEFINES a
  pivot - consumes the entire swing before the spread is even charged.
- Long/short split is symmetric around the index drift (LONG +1.18,
  SHORT -2.17 gross at 3xATR), i.e. no swing structure beyond drift.
- 3.0xATR reversal reproduces the EA ZigZag cadence exactly (median leg
  17 bars, 49 legs/1000 bars vs the EA measured 17 and 44).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:34:33 -04:00
AnimateDread
f3bded779a feat(research): pooled meta-labeling verdict tooling - memmap loader, per-symbol norm, threshold dose-response
Three additions to meta_pool.py, in the order the campaign needed them:
- memmap + float32-throughout (per-batch float64 cast): the 6.5 GB
  4-symbol corpus OOMed the float64 pipeline on the training box;
- pool2: per-symbol standardization (each symbol by its own train-slice
  mu/sd) + 64/32 capacity + l2 1e-3, after the naive pooled model
  underfit to the prior (train CE pinned at base-rate entropy);
- curve: fixed-ladder precision-vs-threshold on calib and test side by
  side - the dose-response diagnostic that closed the question.

RESULT recorded in memory: pooling transfers real skill (XAUUSD +2.6pp,
SP500 +1.2pp at fitted thresholds, >>2 sigma) but 0/8 fitted operating
points clear break-even, and the high-conviction tail is temporally
unstable - the precision-vs-threshold slope FLIPS SIGN between calib and
test on 3 of 4 symbols, so no ex-ante threshold rule exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 08:41:24 -04:00
AnimateDread
f878ea4c75 feat(research): optimize memory usage with np.memmap and improve data handling in MLP 2026-08-14 07:35:29 -04:00
AnimateDread
a7c31e2352 feat(meta): increase DB_MaxRowsPerTable to accommodate larger pattern datasets 2026-08-13 19:25:02 -04:00
AnimateDread
35ccb2eb7a fix(meta): sweep corpus arrays grow on demand - the bars*2 cap crashed or truncated
The candidate sweep reserved bars*2 slots and guarded the bar loop with
room for only 2 appends, but every bar can append m_srcCount*2 candidates
(4 families x 2 sides) and STATE-model patterns stay active on most bars.
Two failure modes, both observed on the first multi-chart attach:

- USDJPY/XAUUSD/XTIUSD H1: mid-bar overflow -> "array out of range in
  SignalMETA.mqh (369/379,25)" -> EA dead on the chart, panel frozen at
  "getting ready".
- SP500 H1: the guard tripped exactly at cap (109,508 = 54,754*2), a
  SILENT truncation that dropped the newest bars from the corpus - the
  sweep walks oldest-first, so what fell off was the most recent history.

The arrays now grow 1.5x whenever headroom for one full bar is missing,
the loop runs to completion on every symbol, and a shrink-to-fit after
the loop returns the slack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 18:35:53 -04:00
AnimateDread
10253b581b feat(inputs): private-build defaults = the drop-on-chart meta-pooling workflow
User request: attaching a chart must need zero Inputs-tab edits. Private
(non-Market) build now defaults to AIType=META, all four classic families
ON (they are the sweep's candidate sources), Meta_ExportDataset=true.
Market-build defaults unchanged (AI_NONE, MA/RSI only, no export);
UseDatabaseRanking=true applies to both per the earlier request.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:31:15 -04:00
AnimateDread
1bf3eba68a feat(meta): self-contained corpus - the META chart sweeps the real classic ladders over its own history
The user should not need a tester corpus run per symbol. Every pattern
condition in Signals\Signal{MA,RSI,MACD,Ichimoku}.mqh anchors its reads on
`int idx = StartIndex()` with zero hardcoded indices (verified), so a
name-hiding StartIndex override + EvalShift(i) on CExpertSignalCustom makes
the EXACT live ladder code answer "what would you have fired at bar i" -
the silent-divergence trap that justified the DB corpus does not exist on
this path, and neither do the GMT-offset ambiguity, the DB row caps, or
the wipe procedure.

- CExpertSignalCustom: m_evalShift + StartIndex()/EvalShift() +
  SweepPrepare(bars) (deep-resizes the shared price series); the four
  classic signal classes override SweepPrepare to deep-resize their own
  indicator buffers.
- CSignalMETA::BuildCorpusBySweep: per bar x per source filter, run
  Direction() shifted, harvest the per-side pattern slots + netVote into
  the same corpus arrays the DB loader fills; entry=bar open so
  MetaPrepareEra's resolution matches at offset +0 with zero price error.
  DB corpus remains the fallback when classic filters are disabled.
- Warrior_EA.mq5: META gets the enabled classic filters as candidate
  sources (family ids match the descriptor one-hot).
- UseDatabaseRanking default false -> true (user request): a META chart
  journals + ranks out of the box.

Workflow per symbol is now: attach ONE chart with AIType=META (optionally
Meta_ExportDataset=true for the offline pool) - candidates, labels,
training and export all happen in place, ~10 seconds of sweep instead of a
tester run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:19:43 -04:00
AnimateDread
932c94e890 feat(research): offline meta-pool pipeline - loader, EA-mirrored splits, MLP, cov x (p-BE) eval
Loads the EA's MetaExport .f32 datasets (UTF-16 sidecars), applies the EA's
own discipline offline: chronological 55/15/30 split with horizon-length
purges, operating point fitted on the calibration slice only via
coverage x (precision - BE) with the 25% floor, test slice touched once,
deployability at the 2-sigma edge floor. Small leaky-ReLU MLP + Adam in
numpy; `stats` / `eval <tag>` / `pool` commands.

First run on XAUUSD_16388 validated the plumbing and exposed the data:
the 2.5h gold tester run only covered 2004-07..2006-10 (3,214 candidates)
because gold tick volume is huge - and corpus builds do not need ticks at
all (journaling is bar-open-keyed, labels come from bar history later), so
"Open prices only" modeling builds the same corpus ~100x faster.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:09:14 -04:00
AnimateDread
d20058fc1b feat(meta): dataset export for offline cross-sectional pooled training
Meta_ExportDataset input: with AIType=META the chart writes its complete
training set once per attach - every resolved+labeled candidate as
[barTime|family|pattern|side|won|NetInputWidth floats] using the SAME
window builder, descriptor and label caches pass 2 trains on, so offline
examples are byte-equivalent to the EA's own. Sidecar .meta.csv carries
layout + the geometry/BE the labels were computed at. Files land in
Common\Files\Warrior_EA\MetaExport\<sym>_<period>.f32.

This is the pooling architecture decision: multi-symbol training INSIDE the
per-chart God-class would be the riskiest surgery this codebase has seen;
instead each chart exports, the pooled head trains offline (small dense+BN
net, minutes on this box), is validated per-symbol under the same
chronological splits and coverage x (p - BE) gate, and only a WINNER gets
written back into a .nnw for the EA to load natively (format fully mapped).
Also turns every future meta experiment from a 20-minute tester cycle into
minutes of offline iteration.

Cost-model note for the record (user challenge, verified): spread is 0.099
ATR = ~2% of the 4.74 ATR trade width - tiny per bar, but expressed in
win-rate points it is 0.099/4.74 = 2.1pp, which is the measured base-vs-BE
gap and the size of the entire observed skill lift. Zero-spread relabeling
would put base == BE by construction. Multi-day holds additionally pay swap,
which the label does NOT charge - the true bar is higher, not lower.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 13:14:12 -04:00
AnimateDread
4db223b40e docs: H4 experiment result - H3 (lift does not transfer; single-instrument well closed)
999 eras on 13,436 H4 candidates. The cost gap halved exactly as computed
(BE 64.1 vs base 63.0 = 1.1pp) and the lift did not come with it: max
cov x (p-BE) = +0.07 in 1 era of 999, selective-threshold skill +0.47pp
mean (noise), OOS ranking slightly inverted (skips won 67% vs 62% for
trades) so the expectancy fitter correctly pinned coverage at 100%. The
lift shrank faster than the cost - the tick-flow decay shape, now measured
at the setup-conditional level. SP500 is closed at both accessible cost
points under pre-registered hypotheses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 13:06:46 -04:00
AnimateDread
0fb53d8979 feat(meta): pin the corpus DB to the chart's symbol+timeframe; pre-register the H4 experiment
The S2 verdict localized precisely: the meta head's edge x width (0.02 x
4.74 ATR = 0.095 ATR/trade) equals the measured spread (0.099 ATR/trade) -
real signal, consumed exactly by cost. The breakdown line adds: the lift is
LONG-ONLY (shorts anti-selected) and MA-family-strongest (67-70% traded win,
<1 sigma over BE on ~350 trades, best-of-32 cells - not family-wise
evidence).

Next experiment, pre-registered in Meta_Labeling_Design.md before any H4
data exists: SP500 H4 doubles ATR against a fixed spread, halving the cost
drag (~1.3pp) that the ~+2pp lift must clear. Same pipeline end to end;
deployability still decided by the unchanged 2-sigma gate. H3 (the honest
risk) is that the lift decays with timeframe as fast as cost does - the
tick-flow failure shape - which would close the single-instrument well and
leave cross-sectional pooling as the only lever.

Enabler fixed here: LoadMetaCorpus picked the LARGEST .db on disk, so an H4
chart would have adopted the (bigger) H1 corpus and resolved candidates onto
wrong bars - and a chart could even adopt another SYMBOL's corpus. The
loader now requires a <symbol>_<period>_ filename match and says so when
nothing matches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 11:11:38 -04:00
AnimateDread
0adaea48b6 fix(resume): model reload stalled training - three hardenings on the resume path
A resumed META model hot-looped pass 1 (0->100% scan oscillation, silent for
3 minutes until the stall reporter fired) because EVERY window failed at the
first AD/Wyckoff feature: the init-time param adoption called
ReInitADIndicators unconditionally, destroying five freshly-calculating
indicator instances to recreate them with BYTE-IDENTICAL params (verified by
parsing the .nnw header - the MI tuner had kept the configured settings), at
process start, on a box with 1 GB free of 31. The replacements sat cold for
6+ minutes while full-history resweeps starved the indicator threads harder.

- AdoptIndicatorParams: installs a loaded param set into the tuner and
  rebuilds handles ONLY when the set actually differs from what the live
  indicators run. Both call sites (resume init + panel reload) use it.
- Resumed models get the same 3 warm-up passes as fresh ones. The skip was
  the shared root cause of the cold-ATR (ba13eef), cold-AD (2026-08-11) and
  this incident - custom indicators recompute from scratch every process
  start regardless of what the .nnw proves.
- Cold-sweep backoff: a pass-1 sweep in which every window failed on a
  TRANSIENT cause arms a 5s era-start pause instead of an immediate
  full-history resweep, so the retry loop stops consuming the CPU/memory the
  warming indicators need. The stall reporter names the backoff branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 10:23:11 -04:00
AnimateDread
df48c37f65 feat: per-family x per-side OOS breakdown in the META era report
350-era S2 verdict on SP500 H1: the meta head carries REAL ranking skill
(+1.0-1.3pp mean over base, 101/350 eras clear their own 2-sigma bar, traded
subset wins 66.1% at <30% coverage vs 64.5% base) but 0/350 eras produced a
positive cov x (p - BE): the candidate stream sits 3pp under the derived
geometry's 67.5% break-even and ~2.6pp of recovered skill cannot bridge it.
Skill plateaued by mid-run (1.28pp -> 1.05pp), so more eras only buy
multiplicity, and the deploy gate correctly shipped nothing.

The aggregate can hide a deployable subset (one family/side clearing BE
blended with junk), so the META era line now decomposes the SAME traded
population into MA/RSI/MACD/Ichimoku x LONG/SHORT cells, each as
traded/candidates base->traded win rate. 32 cells is a best-of-N search by
construction - any candidate cell faces the family-wise rule before belief.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 09:03:40 -04:00
AnimateDread
444909d0a3 feat: S2 meta-labeling head - binary trade-quality model over the classic-candidate corpus
The NN now has a target that is not per-bar direction (closed, best-of-999
p=1.0000): P(win | this journaled candidate, at the EA's own SL/TP, net of
cost). One net for all 52 pattern-sides, AIType=AI_META.

- NetForward.mqh: the host-side softmax+CE gradient generalized total==3 ->
  2||3 on both backprop paths; a 2-class softmax IS a logistic head, and no
  compute backend changes.
- SignalMETA.mqh (new): corpus loaded read-only from the LARGEST signal DB on
  disk (decoupled from the config fingerprint that burned four S1 runs); the
  GMT->server offset is measured PER ROW against entryPrice vs bar open
  (DST-immune, histogram logged); a window-span regime filter drops the
  pre-2017 daily-backfill rows; 31-feature setup descriptor appended at the
  input (26 one-hot + side + tanh netVote + SL/TP ATR + spread/ATR).
- Training.mqh: candidate-queued pass 1, binary-target pass 2, per-candidate
  calibration (2.5) and OOS (3) walks. Counter mapping win->Buy / loss->Sell
  lets checkpoint selection, the edge floor, the plateau ladder and the
  family-wise deploy gate run UNCHANGED: precision reads as win rate among
  traded candidates, chance as the base win rate, recalls as sensitivity/
  specificity. Era-end META line: coverage x (p - break-even) vs the null.
- Labels are the side-conditional triple-barrier win caches - never the DB's
  stop-and-reverse outcome. Logit adjustment deliberately skipped (~40% base
  rate). Live inference + online learning guarded off until S3.
- Fingerprint: conditional |TGT:META1; State\META\ folder + 2-output filename
  slot keep meta models fully separate from direction models.

Compiles clean (0 errors, 0 warnings). S2 run = attach a chart with
AIType=AI_META; S3 wires the votes via the per-side hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 06:52:31 -04:00
AnimateDread
68459208bf fix(meta): make the stale-DB corpus warning unmissable in the tester
The warning lived inside the VerboseMode-gated corpus report, so a
forgotten wipe silently voided an entire 18-year corpus run - the
outdated-row guard rejected the whole replay against leftover rows
and the run appended 35 rows instead of building a corpus. The check
now runs unconditionally at tester OnInit (MetaCorpusStaleCheck): 52
quiet one-row newest-key probes vs the test start, with a loud stop-
wipe-rerun instruction when the DB is newer than the test. Absent
tables probe quietly via FetchNewestTimeKey''s new quiet flag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 23:56:26 -04:00
AnimateDread
aeed4037d5 fix(db): add the missing FetchRecordCount passthrough on CDatabaseManager
6819bb4 called dbm.FetchRecordCount() from ProcessSignal, but the
method only existed on CDatabaseOperationsManager - CDatabaseManager
never exposed it (nothing outside the DB layer had needed it before).
The 12 compile errors were the usual MQL cascade from one unknown
member.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 19:02:11 -04:00
AnimateDread
6819bb4133 perf(db): targeted SQL lookups replace full-table fetches per signal
The historical 1000-row cap existed for a real reason: ProcessSignal
pulled BOTH full tables into MQL struct arrays on every buffered
signal, and UpdateSignalsWeights pulled all 52 per cycle -
materializing thousands of string-bearing structs per event is the
practical limit the cap protected against (SQLite itself has none).
Raising the cap for an 18-year meta-label corpus build would have
made runs crawl; sharding across databases would re-read the same
rows and inherit the same cost.

Every question is now answered inside SQLite, one row or one number
per query, flat in table size:
- FetchOpenTradeEntry: the open (NA) trade''s entryPrice for
  pattern+direction, LIMIT 1
- FetchNewestTimeKey: newest row''s yyyymmddhhmm via max ROWID
  (rows insert chronologically) - the duplicate/outdated guard
- FetchWinLossCounts: COALESCE''d SUM aggregates with the
  before-now bound applied in SQL, replacing the tester-only array
  trim (now also active live, where it is harmless by construction)

ProcessSignal semantics preserved exactly: prune -> close opposite
(stop-and-reverse still registers its own row) -> duplicate/outdated
-> one-open-trade -> register. CalculatePatternWinRate''s array walk
becomes WinRateFromCounts; the private FetchTradeRecords wrapper and
ShouldDeleteOldestEntry are gone. DB_MaxRowsPerTable=20000 is now
cheap at any table size.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 18:53:04 -04:00
AnimateDread
7d3c20d7f1 fix(meta): warn when a corpus backtest starts before the DB''s newest row
The first corpus build produced 3,681 rows, all 2026, from an 18-year
backtest: ProcessSignal''s outdated-row guard rejects any registration
older than a row its table already holds (correct for a live stream),
so a tester run starting before the leftover rows'' dates silently
registers nothing for the overlap. The corpus report now prints a
loud WARNING when running in the tester with DB rows newer than the
test''s start: corpus builds start from an empty DB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 16:20:53 -04:00
AnimateDread
4507ea69a9 feat(meta): S1 - the signal DB becomes the meta-label training corpus
Implements stage S1 of Meta_Labeling_Design.md, superseding the
original "training-time ladder sweep": the per-side journaling from
652bf81/195be20 already produces the exact candidate stream a sweep
would compute - every pattern instance the live ladders fire, both
sides, uncensored, with netVote and touchable entry price - so the
corpus is READ from the DB instead of re-implementing 26 ladder
conditions in training code. That eliminates the silent-divergence
trap outright: the corpus is by construction identical to live
behaviour. Accepted costs are documented in the module and the doc:
coverage equals the populating backtest, and sampling is one
candidate per fire-stretch (the right dedup for training anyway).

- Expert\AIBase\MetaCorpus.mqh: CMetaCorpus reader (52 tables ->
  SMetaCandidate rows) + VerboseMode OnInit report: volume/closed/
  S&R-win-rate per family, span, and the GMT->server bar-offset
  match table (offsets +0..+3h) that S2''s label plumbing pins to -
  measured, not assumed.
- DB_MaxRowsPerTable input (default 1000 = old MAX_TABLE_ROWS): a
  corpus build raises it (e.g. 20000) so a 15-20 year backtest
  isn''t pruned; wired through CExpertSignalCustom::MaxTableRows().
- Report-only stage: nothing downstream consumes the corpus yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 15:20:33 -04:00
AnimateDread
4937d4763b docs: meta-labeling design - the NN predicts trade quality, not direction
Agreed architecture pivot: classic patterns become candidate
generators (WHEN), the excursion head keeps geometry (SHAPE), and a
new meta-head predicts per-instance P(win at the EA''s own geometry)
(WHETHER), replacing the AI direction vote whose question is measured
closed. One net for all patterns, setup descriptor appended to the
existing feature window, triple-barrier side-conditional labels,
existing optimizer/selection/gate machinery reused. Honest floor
stated: if primaries carry zero structure the head degenerates to a
vol/cost/session timer - bounded, and the family-wise gate decides.
Four compile-alone stages; cross-sectional pooling is the next lever
after S4. No SQX parsing anywhere in this path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 15:08:41 -04:00
AnimateDread
b06fdd2f0e research: point the seasonal screen at the five breadth instruments
seasonal.py gets a frame-based entry (analyse_frame) and a reusable
report() so the identical statistics - circular-rotation family-wise
null, max-|t| bar, split-half - can run on instruments whose book is
synthesised from M1 bars. breadth_seasonal.py runs it on the five
SQX-decoded instruments (FTSE100, UK100, WTI x2 feeds, USDCAD) that
share no data path with the four originals; the duplicate-market pairs
(FTSE100/UK100, WTI_d/WTI_5) double as replication checks. Caveats
stated in the module docstring: synthesised flat spread (move/spread
is approximate, no intraday spread shape) and file-time clock labels;
drift/t columns are spread-free and unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 14:08:05 -04:00
AnimateDread
83fa5e4d3d fix(db): a completed DML step is not a failure
Every INSERT/UPDATE a backtest journaled printed a phantom "Failed to
execute bound query (error 5126)" + "Failed to insert/update" pair -
11.7k error lines in one tester run - while every row landed
correctly (verified: v5 DB complete and identical in totals to v4,
results populated, zero non-5126 database errors in the whole log).

5126 is ERR_DATABASE_NO_MORE_DATA, SQLite''s DONE: DatabaseRead()
stepped the statement to completion and there is nothing to read back,
which for DML IS the success outcome. The tester agent reports 5126
where the live terminal reports 0 for the same completed step, and
PrepareAndExecuteBound() treated any nonzero code as failure. Success
is now 0 or 5126; genuine failures (busy, locked, constraint, misuse)
surface as other codes and still fail.

No schema or semantics change - the v5 database and its data are
valid as-is; this only stops the misreporting that would bury a real
error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 13:26:40 -04:00
AnimateDread
195be2025b fix(db): the log no longer asks the decision layer for permission
The DB system logs objectively; the decision layer reads it to compute
win rates and adjust weights. The journaling path still had one
decision-layer tendril: rows were only written when the root''s
OpenLongParams()/OpenShortParams() succeeded. Those calls validate
ORDER PLACEMENT (broker stops-level, ATR warm-up, entry-mode
rejection) and their failures cluster in volatility/spread conditions,
so the gate non-randomly censored exactly those bars out of every
pattern''s win-rate sample - the same censoring class 652bf81/c8ef478
removed, one layer down. The ledger never needed placement to be
possible: entries are marked at the touchable side of the spread and
exits are same-pattern reversals, not broker fills.

Also documents netVote for what it is: a record of the decision
layer''s state at log time (per-pattern weights inside it drift as
ranking updates land), not an objective measure - the objective part
of a row is pattern/direction/price/result.

SIGNAL_DB_SEMANTICS_VERSION 4 -> 5: row populations gain the
previously censored bars, so the database re-keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 12:02:05 -04:00
AnimateDread
c8ef478ce8 fix(db): reversing signals register their own trade (true stop-and-reverse)
Verification of 652bf81 on a fresh 7-month backtest DB surfaced the
last one-sided mechanism: ProcessSignal absorbed a reversing signal as
the exit of the opposite trade and skipped registering it. For pure
EVENT patterns that strictly alternate (MACD model 3, the zero-line
cross), every reversal was consumed and the whole ledger landed on
whichever side fired first - 60 Buy rows, 0 Sell rows - so the silent
side never earned a win rate and UpdateSignalsWeights() weighted the
pattern from one side only. State patterns escaped by re-firing one
bar later.

The reversal now closes the opposite trade AND registers its own row;
the existing duplicate/outdated/open-trade checks still bound the
table at one open trade per pattern+side. Row populations change
meaning, so SIGNAL_DB_SEMANTICS_VERSION 3 -> 4 re-keys the database
(the v3 file is orphaned, not wiped - schema is unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 11:32:36 -04:00
AnimateDread
652bf81112 fix(db): per-side pattern journaling + versioned journaling semantics
The labelMatchesVote gate compared a single last-writer-wins label
(LongCondition then ShortCondition) against the net vote sign, which
structurally censored the pattern tables: a long event co-occurring
with any short-side state model lost its label to the later writer and
was dropped, while the mirrored short event journaled fine. Ichimoku
models 0/3 and MA model 1 could not produce a row at all by
construction (MA model 1 was "revived" in 8710240 yet still could
never journal - its weight-10 vote is exactly cancelled by the
opposing Pattern_0 state), and every pattern's win rate was measured
on a with-trend-only subset - the exact statistic
UpdateSignalsWeights() feeds back into the weights, self-sealing:
no rows -> no win rate -> default weight -> still censored.

- Direction() now evaluates the two ladders separately and snapshots
  each ladder's matched pattern into its own side slot; each side that
  matched journals its own row. The flat-vote poisoning the old gate
  fixed stays fixed: a label can no longer contradict its side.
- The filter's net vote (raw pattern-weight units) is stored as a new
  netVote column - data, never a drop filter. Snapshot is keyed on the
  ladder setting a label, not on its weight, so a 0%-win-rate pattern
  keeps journaling and can recover.
- SIGNAL_DB_SEMANTICS_VERSION is folded unconditionally into the DB
  filename fingerprint: pattern-definition changes (b2069bc, 8710240)
  re-key the database instead of blending incompatible Pattern_N
  populations under one key, which the input-hash fingerprint cannot
  see. 7 months of mixed-semantics rows shared one file because of it.
- dbVersion 2.0 -> 3.0: schema changed, and inserts carry the new
  column, so the version-mismatch folder wipe is the migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 10:37:57 -04:00
AnimateDread
36e8463310 refactor: derive history bars for input sequences and update related configurations 2026-08-11 21:53:37 -04:00
AnimateDread
923addf574 feat: pin the cross-asset pair set train->serve + warm the sync at init
The reference-pair set was re-discovered from Market Watch on every
build, so adding or removing a terminal symbol silently changed what a
trained model's six cross-asset features meant - the last open
train/serve parity gap from the 2026-08-11 audit. The set a model's
FIRST successful build actually used is now stamped into its .cfg
(append-and-length-guard, adopt-don't-compare - the derived-barrier
pattern) and every later build constructs the panel from exactly that
list; a pinned pair that is temporarily unavailable is skipped, never
substituted.

Also warms SymbolSelect/SeriesInfo for every reference symbol at
InitNeuralNetwork, so the terminal's ~minute of async cross-symbol
download starts at init instead of when the first Build() trips over
an unselected symbol - the source of the startup 'only 0 usable
reference pairs' console failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 21:29:14 -04:00
AnimateDread
ccc3dce69e feat: index-mode cross-asset encoding - base==quote wasted 3 of 6 slots
On a CFD whose base and quote currency match (SP500 -> USD/USD) the FX
encoding degenerated: base and quote strength were the SAME series twice
and the divergence feature collapsed to the symbol's own 20-bar return.
Index mode re-encodes the six slots: denomination-currency strength
(fast/slow), a risk-proxy currency's strength (JPY by fixed preference
order - deterministic across rebuilds), and divergence as own move minus
what the denomination alone implies. FX-pair symbols are untouched.

Fingerprint gains :IDX2 for base==quote symbols only, so index models
trained under the degenerate encoding re-key while FX models keep their
filenames. FORCES RETRAIN on index/CFD charts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 21:07:52 -04:00
AnimateDread
53ccc03453 fix: the trailing-incumbent count gate was unpassable by construction
passTrail demanded m_excTrailScored >= EXCURSION_MIN_SCORED (500), but since
e2c9593 the trail race only scores DISJOINT bars: m_excTrailScored is bounded
by m_excScoredD (~OOS/horizon ~= 256 on SP500 H1) minus the post-ring-clear
warm-up (~8), so every chart failed "[trailing incumbent not warm enough to
race]" at 247-248 of a possible ~256 forever - observed live 2026-08-11 on
all four charts. The counter's statistical population is the same disjoint
sample passDj gates on, so it now takes the same minimum
(EXCURSION_MIN_DISJOINT, 200), reachable with margin after warm-up.

Compile: 0 errors, 0 warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:50:39 -04:00
AnimateDread
bd1037975a fix: the trailing incumbent read the future across eras; cold AD blocks cached zeros as truth
Three findings from the 2026-08-11 audit:

1. The excursion head's trailing-quantile ring was deliberately never cleared
   between eras ("a rolling estimate of the market, not of the era") - but
   pass 3 re-walks the SAME OOS window every era, so at each walk's restart
   the ring still held the outcome masks of the newest OOS bars from the
   previous walk: the chronological FUTURE of the bars about to be scored.
   For the first ~window+horizon pushes of every era the "trailing" incumbent
   was partly a leading one - conservative for the gate (an informed incumbent
   is a harder hurdle) but exactly the self-made-artifact class 06d4785 hunts.
   The ring now clears at era-score reset; the warm-up bars simply don't score
   the trail race, which the m_excTrailN gating already accounts for.

2. skillTrail compared the head's FULL-block Brier (pro-rated by coverage)
   against the incumbent's subset sum - valid only if head skill is uniform
   across the OOS walk, while the trail-scored subset systematically excludes
   each era's warm-up bars. The audit also found m_excBrierHeadD/BaseD/
   m_excOosHitsD declared, zeroed and never accumulated (dead since e2c9593
   made every scored bar disjoint). The dead trio is replaced by
   m_excBrierHeadT: the head's Brier accumulated only on the bars the warm
   incumbent also scored, so the race now compares both predictors on an
   identical bar set.

3. The AD/Wyckoff feature blocks read GetData with no EMPTY_VALUE guard; a
   cold (still-calculating) indicator returns EMPTY_VALUE everywhere, the
   sanitize loop rewrote that to 0.0, and the bar SUCCEEDED - so
   BufferTempData cached an all-zero Wyckoff block as a success for the whole
   bar frame: the one path the f6150ee only-cache-successes rule cannot see,
   because it never fails (the ba13eef class, arriving through values that
   never fail; a resumed model's era-0 prebuild starts milliseconds after
   OnInit). ADIndicatorCold() probes the NEWEST bar - EMPTY_VALUE there means
   async warm-up (transient reject, retried), while deep bars beyond the
   buffered depth keep the sanitize loop's neutral-fill so degraded history
   still trains. Also fixed m_featureCacheValid's declaration comment, which
   still described the pre-f6150ee cached-miss semantics.

Compile: 0 errors, 0 warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:20:24 -04:00
AnimateDread
77e8080cfe fix: four risk-layer holes a funded account would eventually find
1. The expectancy stop was stone dead at shipped defaults. Its only feed -
   RecordTradeResult inside CTradeJournalManager::Update() - ran solely under
   UseDatabaseRanking, which ships false, so the da54639 halt was armed
   (ExpectancyMinTrades=40) and never received a single closed trade. A risk
   rule must not be a side effect of an analytics toggle: the journal gains
   InitTrackingOnly(), Update() runs unconditionally from OnTick and skips
   only the DB insert when no DB was initialized.

2. Below-minimum lots were silently bumped UP to SYMBOL_VOLUME_MIN by
   TCNormalizeVolume - correct for a user-entered fixed lot, but in the
   risk-sizing path it turned a budget-capped 0.05 into 0.10 on min-0.10/
   step-0.01 symbols: double the intended risk, after CapRiskAmount already
   clamped, exactly the routine-stop-out-breaches-the-daily-limit scenario
   the budget exists to close. CMoneyRiskBase now refuses the trade when the
   risk-derived lot is below the broker minimum.

3. All trading was async fire-and-forget (SetAsyncMode(true)) with no
   OnTradeTransaction handler and no retry: server retcodes were never
   observed. Fail-safe for entries, not for closes - a silently rejected
   close rode the position until the next bar (or next day for the timed
   close window). Now synchronous, matching the risk-budget flatten's own
   already-synchronous CTrade; on an H1 EA the latency is irrelevant.

4. FIXED_LOT bypassed the budget entirely (no CapRiskAmount, no
   OpenRiskAtStops) - pre-halt it could commit more than the remaining daily
   allowance. A fixed lot cannot be scaled, so the rule is binary: its
   loss-to-stop fits the remaining allowance whole or the trade is refused;
   unpriceable risk (no SL) is refused while the budget is enabled.

Compile: 0 errors, 0 warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:14:26 -04:00
AnimateDread
0848c8a16c fix: live inference queried the 1-tick forming bar - a window training never built
RefreshLatestSignal ran at the first tick after a bar opens and built its
window at r=0: series index 0 at that instant is a candle with one tick of
data - (close-open)/atr ~ 0, high ~ low, degenerate volume, indicators on a
1-tick bar. Training never produces such a window (every labeled bar is fully
closed, entry at that bar's CLOSE), so the deployed model's final timestep -
the one the LSTM/HYBRID output is keyed to - was out-of-distribution on every
live decision, and pass 3's deploy-gate OOS scores measured a different query
than live executed. The parity index is r=1: the newest CLOSED bar, whose
close IS the current price - the exact instant the label's hypothetical entry
happens. Single backtests shared the old skew (same r=0), which is why the
tester agreed with live while both disagreed with training.

Bookkeeping split that the index change forces: m_lastBarTime/dtStudied stay
anchored to the FORMING bar's open (they gate against SERIES_LASTBAR_DATE;
anchoring at bar 1 would re-fire the refresh every tick), while bt - the
arrow, its High/Low placement, and NMS declustering - anchors to the decision
bar, now matching the rescan path's convention.

Also: a failed refresh no longer trades the previous bar's signal for the
whole bar. RefreshLatestSignal returns success, zeroes dPrevSignal on failure
(no opinion beats a stale one), and RefreshConvergedSignal advances dtStudied
only on success so the next tick retries - the tester path (m_lastBarTime)
already worked this way; this is the live path catching up.

FORCES RE-VALIDATION of deployed models: the effective live query distribution
changes. Bundled with the backprop transpose fix's retrain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:10:23 -04:00
AnimateDread
ea9d86b3ee fix: dense backprop read the weight matrix transposed - on every backend
CaclHiddenGradient computed this layer's gradient as matrix_w[(outputs+1)*i + k]
against a buffer whose actual layout (one row per NEXT-layer neuron, stride
inputs+1) makes the correct read matrix_w[k*(inputs+1) + i]: the transpose for
square layers, and for the non-square boundaries this EA actually builds
(tapered stacks, the 3-neuron head) a mis-strided walk that ran past the buffer
end - garbage on OpenCL, zeroed reads on the CPU DLL, so the tiers did not even
agree with each other. Every gradient crossing a dense boundary on its way down
- the entire learning signal reaching the BN/conv/LSTM front ends - passed
through a fixed wrong matrix: feedback-alignment dynamics, not backprop, which
is why nets still "learned something" and this survived. The book reference
(NeuroNet_DNG) fixed this in a later article version; our kernel descended from
the earlier one. Confounds every model-based negative verdict to date.

Also in this commit, same root cause family:
- per-sample UpdateWeightsAdam (OpenCL): input for slot group j was read at
  matrix_i[j] instead of matrix_i[j*4] (corrupted outer product past group 0),
  and dispatch dim 1 sized on ceil(inputs/4) left the bias column unreachable
  whenever inputs%4==0 - dense biases never trained on OpenCL. Rewritten as a
  lane-guarded scalar loop keeping our Adam conventions (sqrt-stored v,
  decoupled decay, both clamps, no sign gate). The batched accum path never had
  either bug; this kernel is what SetBatchSize(1) runs - including online
  continual learning on client machines, where OpenCL is the only tier.
- conv backward passed raw (int)Activation() where the kernels expect
  NativeActivationCode(): NONE took the tanh branch (clamping a BN layer's
  unbounded z-scores), TANH took sigmoid, PRELU took none. Dormant only because
  the conv sits at layer 1 today.
- hidden-gradient dispatch over Neurons()+1 dropped to Neurons(): biases get no
  backprop gradient and the extra work-item only ever read past matrix_o.

All three backends (Network.cl, WarriorCPU.cpp, WarriorDML.cpp HLSL) changed in
lockstep; DML gained an `inputs` constant to derive the row stride. New
dense_backprop_check.cpp proves the CPU kernel is central-finite-difference
consistent with the real forward kernel on 8x8, 64x3, 33x64, 5x3 (max diff
3e-9) and that all three activation branches match transcription. All 16 checks
pass. Offline math check only - the in-situ proof remains the per-layer dW/W
report on a real era.

FORCES FULL RETRAIN. Both DLLs rebuilt and redeployed to MQL5\Libraries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:06:09 -04:00
AnimateDread
a9677bb538 perf: the CPU DLL's dot products were never vectorized - /fp:fast
Measured on this machine's actual CPU at the real 760-wide geometry, through a
real DLL boundary (an earlier harness #included the .cpp and the fast-math
build hoisted the timing loop, reporting a flat ~4us for shapes 8x apart).

Every hot kernel is a floating-point reduction. Under the default /fp:precise
MSVC may not reassociate one, so it cannot vectorize one - the dot product was
scalar mulsd/addsd through a single accumulator. Forward pass measured
1.1-1.9 GFLOP/s precise vs 1.7-2.8 GFLOP/s fast, and the same three
neurons*inputs loops (forward, hidden gradient, weight-gradient accumulate)
dominate an era.

batch_accum_check passes on both builds with identical output to every digit
it prints, including the 5-decade optimizer scale-invariance sweep. The
deploy-time CPU-vs-MQL5 self-check tolerance is 1.0e-3, ~11 orders looser than
fast-math drift.

/arch:AVX2 is now explicitly forbidden in the script with the reason. This CPU
is an Ivy Bridge-EP Xeon: AVX yes, AVX2/FMA no. An AVX2 build faults on every
kernel, SehCallFn swallows it per dispatch, buffers are never written, and
every shape takes a flat ~5us - which benchmarks as a 250x speedup until you
check that the outputs are all zero. /arch:AVX alone was measured and bought
nothing; these loops are memory-bound and Ivy Bridge splits 256-bit loads
into 2x128 anyway.

Requires rebuilding WarriorCPU.dll (build_cpu.bat) - the .ex5 is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:20:12 -04:00