2026-08-12 15:08:41 -04:00
|
|
|
# Meta-Labeling: the NN predicts trade quality, not market direction
|
|
|
|
|
|
|
|
|
|
**Status: DESIGN — agreed 2026-08-12, implementation staged below.**
|
|
|
|
|
|
|
|
|
|
## Why this target
|
|
|
|
|
|
|
|
|
|
Per-bar direction of one instrument from its own H1 history is measured dead three
|
|
|
|
|
detector-independent ways (MI noise floor, lag profile 0–20, post-backprop-fix retrain at
|
|
|
|
|
chance vs a positive control that finds volatility at 4x its null). No topology change can
|
|
|
|
|
extract information that is not in the input-target pair. Every credible "NN works on
|
|
|
|
|
markets" result changes the **target** or the **data** instead. This design changes the
|
|
|
|
|
target; cross-sectional pooled training (change the data) is the follow-up lever, and a
|
|
|
|
|
quantile head can ride along on the retrain either forces.
|
|
|
|
|
|
|
|
|
|
The new question the NN answers: **given that a specific setup just fired, will THIS
|
|
|
|
|
instance reach its target before its stop, at the EA's own geometry, net of cost?**
|
|
|
|
|
That is a well-posed supervised problem where per-bar direction never was:
|
|
|
|
|
|
|
|
|
|
- the label is the realised outcome of an actual trade shape, not a synthetic class;
|
|
|
|
|
- the base rate is the setup's win rate (~40–65%), not a 1% tail;
|
|
|
|
|
- part of the answer is PROVEN predictable (range at 4x null; spread/vol/session state),
|
|
|
|
|
because "does the target get hit first" is partly a volatility-geometry question;
|
|
|
|
|
- every labeled example is a moment someone would actually consider trading.
|
|
|
|
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
classic patterns (26 x Buy/Sell) -> candidate: (side, pattern, geometry) [WHEN]
|
|
|
|
|
|
|
|
|
|
|
excursion head (proven) -> SL/TP geometry for the candidate [SHAPE]
|
|
|
|
|
|
|
|
|
|
|
META-HEAD (this design) -> P(win | candidate, market state) [WHETHER]
|
|
|
|
|
|
|
|
|
|
|
vote = f(P - break-even) -> existing threshold / MM / risk plumbing [HOW MUCH]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- **Primaries**: the existing classic signals. They are NOT assumed to carry edge — they
|
|
|
|
|
define when a candidate exists. SQX remains the user's separate rule factory; nothing
|
|
|
|
|
here parses or reverse-engineers SQX. More primaries (e.g. the user's Wyckoff blocks)
|
|
|
|
|
are added only after the meta-head demonstrates skill on the current 52.
|
|
|
|
|
- **One net for all patterns**, pattern identity as input features — shares representation
|
|
|
|
|
across patterns, and event-rare patterns borrow strength from state-common ones.
|
|
|
|
|
- **Integration point**: the per-side pattern slots added in `652bf81`
|
|
|
|
|
(`GetActivePatternLong/Short` + `LastNetVote`) are exactly the hook: the meta-signal
|
|
|
|
|
reads which candidates fired this bar and emits a gating vote for that side. The AI
|
|
|
|
|
filter's global direction vote (closed verdict) is what it replaces.
|
|
|
|
|
- **Unchanged**: excursion head, expectancy stop, risk budget, the DB as objective logger
|
|
|
|
|
with its decision layer (the DB keeps ranking primaries; the meta-head is a second,
|
|
|
|
|
richer consumer of the same "did this instance pay" question).
|
|
|
|
|
|
|
|
|
|
## Training
|
|
|
|
|
|
|
|
|
|
- **Sample**: bars where at least one pattern fired, found by sweeping the classic ladders
|
|
|
|
|
over training history (the ladders are already index-based; a historical sweep is cheap).
|
|
|
|
|
One training row per (bar, fired pattern, side).
|
|
|
|
|
- **Label**: triple-barrier outcome for the proposed side at the EA's own SL/TP geometry —
|
|
|
|
|
the existing labeler (`b4a704d` line), evaluated from the fire bar. Win=1 / loss-or-
|
|
|
|
|
timeout=0. Same horizon rules as current training.
|
|
|
|
|
- **Features**: the existing `BuildFeatureWindow()` output (unchanged front end), plus a
|
|
|
|
|
setup descriptor appended at the head: pattern one-hot (26), side, the filter's own
|
|
|
|
|
netVote, SL and TP distances in ATR, spread/ATR at fire time.
|
|
|
|
|
- **Head**: binary logistic with the existing logit prior-correction (`f2ec1ed`), trained
|
|
|
|
|
with the existing optimizer/batch-norm/mini-batch stack — all of that machinery is
|
|
|
|
|
target-agnostic.
|
|
|
|
|
- **Checkpoint selection**: the existing operating-point objective, coverage x
|
|
|
|
|
(precision − break-even) on the purged calibration slice (`2189316`, `983a6a3`) — it was
|
|
|
|
|
built for exactly this shape of question.
|
|
|
|
|
- **Deploy gate**: family-wise, as always — the final deploy decision is best-of-N over
|
|
|
|
|
eras and must clear the null of the maximum (`1df3054` lesson). No exceptions because
|
|
|
|
|
the idea is likeable.
|
|
|
|
|
|
|
|
|
|
## The honest risk, stated up front
|
|
|
|
|
|
|
|
|
|
If the primaries carry zero conditional structure, the meta-head degenerates into a
|
|
|
|
|
volatility/cost/session timer. That is the FLOOR, not failure — "skip candidates whose
|
|
|
|
|
geometry can't pay in this vol/spread state" is real risk control — but it is bounded and
|
|
|
|
|
must not be sold as entry edge. The experiment's upside case is that sparse conditional
|
|
|
|
|
structure exists at setup moments that the dense per-bar tests could not see (they measure
|
|
|
|
|
marginal per-bar information; a signal alive on 3% of bars is invisible to them by
|
|
|
|
|
construction). The gate decides which case we are in; nothing else does.
|
|
|
|
|
|
|
|
|
|
## Staged implementation (each stage compiles and is testable alone)
|
|
|
|
|
|
2026-08-12 15:20:33 -04:00
|
|
|
1. **S1 — corpus from the signal DB** (implemented; supersedes the original "training-time
|
|
|
|
|
ladder sweep"): the per-side journaling built in `652bf81`/`195be20` means the DB already
|
|
|
|
|
IS the candidate stream — every instance the live ladders fire, both sides, uncensored,
|
|
|
|
|
with netVote and entry price. Reading it (`Expert\AIBase\MetaCorpus.mqh`) instead of
|
|
|
|
|
re-implementing 26 ladder conditions in training code eliminates the silent-divergence
|
|
|
|
|
trap outright: the corpus is by construction identical to live behaviour. Costs accepted:
|
|
|
|
|
corpus coverage = whatever backtest populated the DB (a corpus build is a tester run with
|
|
|
|
|
`UseDatabaseRanking=true` and the new `DB_MaxRowsPerTable` input raised, e.g. 20000, so a
|
|
|
|
|
15–20 year run isn't pruned), and sampling is one candidate per fire-stretch (the correct
|
|
|
|
|
dedup for training anyway). The `VerboseMode` OnInit report prints corpus volume per
|
|
|
|
|
family, closed fraction, and the measured GMT→server bar-offset match table that S2's
|
|
|
|
|
label plumbing pins to.
|
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
|
|
|
2. **S2 — meta-head + label plumbing** (implemented 2026-08-13): `Signals\SignalMETA.mqh`
|
|
|
|
|
(`AIType = AI_META`) + meta seams through the existing training machinery. What shipped,
|
|
|
|
|
including the four deviations from the sketch above and why:
|
|
|
|
|
- **2-output softmax head, not a separate logistic path**: a 2-class softmax+CE is
|
|
|
|
|
mathematically a logistic/BCE head, so the `total==3` host-side gradient in
|
|
|
|
|
`AI\Impl\NetForward.mqh` was generalized to `2||3` (both backprop paths; zero backend
|
|
|
|
|
changes) instead of growing a parallel loss.
|
|
|
|
|
- **Descriptor at the INPUT, not "at the head"**: CNet has no concat layer. On the S2
|
|
|
|
|
MLP front end input-append is equivalent up to one linear layer. 31 features:
|
|
|
|
|
26-slot pattern one-hot, side, tanh(netVote/20), SL/TP in ATR, spread/ATR at fire.
|
|
|
|
|
Conv/LSTM meta variants would need whole-pseudo-bar padding — out of S2 scope.
|
|
|
|
|
- **No prior correction**: the logit adjustment is 3-class-shaped and exists for a ~6%
|
|
|
|
|
tail; the meta base rate is ~40% and the operating-point fit carries calibration.
|
|
|
|
|
- **Trained on the live chart like every other model** (not "offline in the tester" —
|
|
|
|
|
the tester is inference-only by architecture). The corpus is read at attach from the
|
|
|
|
|
LARGEST signal DB on disk (config-fingerprint-independent — the S1 procedural traps
|
|
|
|
|
came from exactly that coupling), resolved to server bars per era with the GMT offset
|
|
|
|
|
MEASURED PER ROW against `entryPrice` vs bar open (DST-immune; histogram logged), and
|
|
|
|
|
pre-2017 daily-backfill rows are dropped by a window-span regime filter.
|
|
|
|
|
- **The whole selection/deploy stack runs unchanged** via a counter mapping (win→Buy,
|
|
|
|
|
loss→Sell): precision = wins among traded candidates, chance = base win rate, buy/sell
|
|
|
|
|
recall = sensitivity/specificity (kills always/never-call collapses), operating point =
|
|
|
|
|
coverage x (win − geometric break-even) on the purged calibration band, deploy gate =
|
|
|
|
|
family-wise as always. Era-end `META` log line reports cov x (p − BE) vs the base-rate
|
|
|
|
|
null with its standard error.
|
|
|
|
|
- Labels are the side-conditional triple-barrier win caches (never the DB's S&R outcome);
|
|
|
|
|
the fingerprint gains a conditional `|TGT:META1` token and meta models live under
|
|
|
|
|
`State\META\` with the 2-output filename slot, so no collision with direction models.
|
|
|
|
|
S2 casts no votes: live inference/online-learning entries are guarded off for the meta
|
|
|
|
|
target until S3.
|
2026-08-12 15:08:41 -04:00
|
|
|
3. **S3 — inference integration**: meta-signal consumes fired candidates via the per-side
|
|
|
|
|
hooks and votes; classic weights stay as a fallback layer; VerboseMode panel exposure.
|
|
|
|
|
4. **S4 — retrain + family-wise gate + (only if passed) deploy.**
|
|
|
|
|
|
|
|
|
|
Cross-sectional pooling (one model over SP500 + the 4 tick + 5 SQX-decoded instruments,
|
|
|
|
|
scale-free features already in place) is the next lever after S4, whatever S4's verdict.
|
|
|
|
|
Snapshot/seed ensembling rides along from S2 (average the last K checkpoint snapshots
|
|
|
|
|
instead of trusting one).
|
2026-08-13 11:11:38 -04:00
|
|
|
|
|
|
|
|
## S2 verdict on SP500 H1, and the pre-registered H4 experiment (2026-08-13)
|
|
|
|
|
|
|
|
|
|
**Measured over 350+370 eras:** the head carries real, persistent ranking skill — the
|
|
|
|
|
first out-of-sample signal since the pivot — but 0 eras cleared `cov x (p − BE)`. The
|
|
|
|
|
decomposition line localized it: the lift is LONG-ONLY (+1.8–2pp; the model's short picks
|
|
|
|
|
do *worse* than the short base rate), strongest on the MA family (67–70% traded win,
|
|
|
|
|
nominally above the 67.5% break-even but <1σ on ~350 trades and best-of-32 cells — not
|
|
|
|
|
family-wise evidence). The clinching arithmetic: edge x width = 0.02 x 4.74 ATR ≈ 0.095
|
|
|
|
|
ATR/trade against a measured spread of 0.099 ATR/trade. **The signal is real and being
|
|
|
|
|
consumed exactly by cost.**
|
|
|
|
|
|
|
|
|
|
**H4 experiment (pre-registered before any H4 data exists):** on SP500 H4 the ATR roughly
|
|
|
|
|
doubles while the spread is unchanged, so the cost drag (BE − base gap) roughly halves
|
|
|
|
|
(~1.3pp) while the lift — if it transfers — stays ~+2pp. Same pipeline end to end: S1
|
|
|
|
|
corpus backtest on H4, AI_META chart on H4. Hypotheses, stated in advance:
|
|
|
|
|
- H1 (primary): `cov x (p − BE)` turns positive at ≥25% coverage, clearing the 2σ edge
|
|
|
|
|
floor — deployability decided by the existing gate, nothing relaxed.
|
|
|
|
|
- H2 (secondary): the lift remains long-only; SHORT traded win stays at/below its base.
|
|
|
|
|
- H3 (risk): the lift decays with timeframe as fast as the cost does (the tick-flow
|
|
|
|
|
failure shape), leaving the net at zero again. If H3 is what the data says, the
|
|
|
|
|
single-instrument well is closed at every accessible cost point and cross-sectional
|
|
|
|
|
pooling is the only remaining lever.
|
|
|
|
|
The known thinning: ~1/4 the bars, so ~11k usable candidates — acceptable for this head.
|
|
|
|
|
The corpus loader now REQUIRES a symbol+timeframe-matching DB, so the H1 corpus cannot
|
|
|
|
|
leak into the H4 run.
|
2026-08-13 13:06:46 -04:00
|
|
|
|
|
|
|
|
**H4 RESULT (2026-08-13, 999 eras, 13,436 candidates): H3.** The cost gap halved exactly
|
|
|
|
|
as computed (BE 64.1% vs base 63.0% = 1.1pp at the derived 3.06/1.71 geometry) — and the
|
|
|
|
|
lift did not come with it: max cov x (p − BE) = +0.07 (1 era of 999, pre-2σ), skill at
|
|
|
|
|
selective thresholds +0.47pp mean (noise), and the head's ranking on H4 was slightly
|
|
|
|
|
INVERTED out of sample (its skipped candidates won 67% vs 62% for its trades), so the
|
|
|
|
|
expectancy fitter correctly pinned the threshold at 0/100% coverage. The lift shrank
|
|
|
|
|
faster than the cost — the tick-flow failure shape, now measured at the setup-conditional
|
|
|
|
|
level too. **The single-instrument SP500 well is closed at both accessible cost points,
|
|
|
|
|
with pre-registered hypotheses.** Remaining levers, in expected-value order: (1) candidate
|
|
|
|
|
streams that carry positive base edge (the user's own SQX strategies' signals as a
|
|
|
|
|
journaled candidate family — the meta head is an amplifier, and it finally would have
|
|
|
|
|
something to amplify; their trade-list CSVs measure this before any build), (2)
|
|
|
|
|
cross-sectional pooling (the last pure-NN lever; honest prior lowered by the twice-
|
|
|
|
|
measured signal≈cost equilibrium), (3) S3 as a variance-reducing risk filter at zero net
|
|
|
|
|
edge.
|
feat(ensemble): per-NN inputs replace the preset selector - the meta head becomes the vote's gate
User design (2026-08-19): 'remove the enum menu that selects neural networks... individual
inputs for every NN just like classic signals... the META NN should be integrated into the
voting decision pipeline when enabled... as a bonus meta labelling is applied to enabled NNs.'
- AI_CHOICE is GONE (tombstoned per the stale-.set doctrine). Use_MLP/Use_CONV/Use_LSTM/
Use_CONVLSTM are ordinary bools like the classic votes; the ensemble arithmetic adapts to
any subset because the consensus divisor is the enabled capable weight. Two or more
enabled = ensemble (|ENS1 token + joint gate, exactly the old AI_HYBRID fingerprints, so
existing weight files keep loading); one = the old solo preset; none = classic-only.
- Use_MetaLabeling un-couples META from the direction NNs (the old selector made them
mutually exclusive). S3 ships: CSignalMETA::LiveMetaGate scores each vote-cleared entry
(shared window at bar 1 + proposal descriptor: side, net vote, live geometry, spread/ATR;
pattern one-hot ZEROED - ranking, not calibrated probability, documented in the body) and
vetoes below the cost-adjusted break-even. Entries only; fail-open everywhere, loudly.
- COEXISTENCE HAZARDS closed: VoteCapableWeight()=0 and ProspectiveVote()=false for the
meta target - solo-only until today, a trained META would otherwise sit in the consensus
divisor as a permanent abstainer and shrink every vote by its module weight.
- CERTIFIED == TRADED: the ensemble era verdict replays the identical veto through the same
g_warriorMetaGate pointer over its OOS fired bars (bar re-resolved from the row's own
time; fail-open counted as fires and reported: 'metaGate: N approved, M vetoed, K
unscored'). The overlay deliberately does NOT replay it (veto-filter-in-replay class,
calendar-cliff precedent) - documented at the sweep site. Solo charts' own gate does not
model the veto - the standing solo-gate caveat, documented at the input.
- DB continuity: the pattern/journal DB fingerprint's first slot was (int)AIType;
DbLegacyAiSlot() maps every legacy-expressible config to its OLD value (new 2-3 member
subsets get 100+bitmask, outside the legacy range) so no existing database re-keys.
filterID becomes the enabled roster via one EnabledNNSummary().
- HUD: the meta line shows the gate (armed/(trn), last P vs BE, ok/veto tally); the
armed/disarmed announcement fires on state change via one latch (MetaGateArmedNow), not
only when an entry happens to be proposed.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:01:02 -04:00
|
|
|
|
|
|
|
|
## S3 shipped as the ensemble-vote gate (2026-08-19)
|
|
|
|
|
|
|
|
|
|
User design: the AI_CHOICE selector is replaced by per-NN toggles (`Use_MLP/Use_CONV/
|
|
|
|
|
Use_LSTM/Use_CONVLSTM`), and the meta head is un-coupled from them (`Use_MetaLabeling`,
|
|
|
|
|
default off): it now runs BESIDE any direction subset and, once its own training
|
|
|
|
|
completes, VETOES vote-cleared entries whose P(win) sits below the cost-adjusted
|
|
|
|
|
break-even (`CSignalMETA::LiveMetaGate`, consulted in `CheckOpenPosition` after the
|
|
|
|
|
threshold). Entries only - exits and the scheduled close-all never consult it. It still
|
|
|
|
|
casts no votes and cannot dilute the consensus: `VoteCapableWeight()` returns 0 and
|
|
|
|
|
`ProspectiveVote()` returns false for the meta target - a coexistence hazard that never
|
|
|
|
|
existed while META was a solo preset. The ensemble era verdict replays the identical
|
|
|
|
|
veto over its OOS fired bars through the same `g_warriorMetaGate` pointer (certified ==
|
|
|
|
|
traded; the era line reports approved/vetoed/unscored). A SOLO direction chart's own
|
|
|
|
|
gate does not model the veto - the standing solo-gate caveat, unchanged.
|
|
|
|
|
|
|
|
|
|
Honesty note: at gate time the pattern one-hot is ZEROED (the proposal is the vote, not
|
|
|
|
|
a classic-ladder fire) - the trained context trunk's pattern-free score. No training row
|
|
|
|
|
ever had an empty one-hot, so the output should be read as RANKING, not calibrated
|
|
|
|
|
probability; the break-even threshold is the least arbitrary zero-knob rule available.
|
|
|
|
|
The measured next lever remains lever (1) above with the vote as the stream: journal the
|
|
|
|
|
ensemble vote itself as a candidate family (VOTE pattern tables), so the head trains on
|
|
|
|
|
the exact fire stream it gates and its scores become in-distribution.
|