feat(ai): logit-adjusted loss, replacing oversampling and the post-hoc prior
Menon et al. 2021 (ICLR), "Long-tail learning via logit adjustment": add
tau*log(prior_c) to each class logit inside the training gradient. Softmax
CE on adjusted logits is consistent for BALANCED error - the metric
checkpoint selection already ranks on - so the loss and the deploy decision
finally optimize the same thing.
The engine already computed a true softmax + categorical-CE gradient and
wrote it over the per-neuron sigmoid delta, so this is an offset added to
three logits in the two places that gradient is built (backProp scalar path
and backPropOCL). No backend, kernel or DLL change; the forward pass and
every inference path are untouched, which is the point - the network learns
to absorb the offset, so its raw argmax becomes the balanced-optimal
decision with nothing applied at inference.
Replaces rather than stacks. Minority replay is disabled while this is on,
and the post-hoc inference prior is forced off. Stacking is not a
theoretical worry: simulated on the measured 1118/1119/34298 distribution
in the weak-signal regime, plain CE collapses to Neutral (33.4% balanced,
Buy 0%), replay reaches 48.1%, logit adjustment 50.9% with better balance -
and BOTH together score 45.4% with Neutral recall at 0%, worse than either
alone. Buda et al. 2018 predicts exactly that.
Motivation from the six-chart run: every topology took one direction to
~50% recall and abandoned the other, the direction chosen arbitrarily (the
batch-norm control went Buy 1% / Sell 42%, the inverse of the other five).
One era in 1,301 cleared the per-class recall floor.
Fingerprinted conditionally, so the converged 60.7% models on disk keep
their filenames and stay loadable as the fallback.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:05:14 -04:00
|
|
|
//+------------------------------------------------------------------+
|
2026-07-22 13:33:56 -04:00
|
|
|
//| Inputs.mqh |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "AnimateDread"
|
|
|
|
|
#property link "https://www.mql5.com"
|
|
|
|
|
#include "..\Enumerations\InputEnums.mqh"
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
//--- Each `input string *_Settings` below is a GUI-only section divider: MetaTrader renders an input
|
|
|
|
|
//--- string whose value equals its comment as a header. Never read by MQL5 code - that's expected, not
|
|
|
|
|
//--- dead wiring. Sections are ordered most-used first: General, Money, Trade, Classic Signals,
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- Neural Network, AI Input Features, Filters, Trade Journal - then NN Optimizer / Performance LAST.
|
|
|
|
|
//--- The Neural Network block sits directly ABOVE AI Input Features because that is the reading order a
|
|
|
|
|
//--- user actually needs: choose the architecture, then choose what it sees. NN Optimizer / Performance
|
|
|
|
|
//--- must remain the final divider in this file - the Adam/Sgd inputs are declared in AI\Network.mqh and
|
|
|
|
|
//--- render immediately after it, so anything added below would land inside that group.
|
2026-07-22 13:33:56 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// GENERAL
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string Expert_Settings = "General"; // General
|
|
|
|
|
input ulong Expert_MagicNumber = 2024; // Magic number (unique EA id)
|
|
|
|
|
input bool Expert_EveryTick = false; // Calculate on every tick
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- NO LONGER AN INPUT (2026-08-01). The detailed panel/journal is a developer view: a buyer does not
|
|
|
|
|
//--- care which plateau stage the ladder is on, and every row in the Inputs tab is a row they have to
|
|
|
|
|
//--- read past to reach something that matters. Same reasoning that already applied to DebuggingMode
|
|
|
|
|
//--- below, so the two now sit together. Flip to true and recompile to work on the EA.
|
|
|
|
|
const bool VerboseMode = false;
|
fix(ui): unique chart tag, product-grade panel, responsive under load
Three separate reports from one deploy.
1. CONV, LSTM and HYBRID all came back tagged [4109]. The weights
fingerprint omits the topology type on purpose - the file path already
separates it (State\CONV\ vs State\LSTM\ vs State\HYB\) and hashing a
value that is constant within a folder buys nothing while re-keying
every trained model into a forced retrain. So the files were never at
risk, but the tag could not do its one job. Prefixing the short id
makes it unique on the display side only; the hex half still greps
straight to the .nnw inside the folder the prefix names.
2. The default panel read like a training console. Six lines down to
three, each answering a question an owner actually has. The deploy
internals (best score, eras-since-best, ladder stage) were developer
diagnostics describing a recall floor that no longer decides anything,
and were already in the era-end journal line. In-sample accuracy left
the panel too: it grades the model on bars it trained on, so it always
flatters, and showing it beside the honest number invites reading the
wrong one. New compile-time DebuggingMode constant - deliberately not
an input - carries the IS/OOS pair and the resolved model path into
the journal instead. No extra Inputs row, no extra Market description
line, no user-reachable firehose.
3. Panel drag and buttons stuttered under training load, exactly as the
2026-07-26 note raising the chunk budget to 200ms warned they might.
Backed off to the documented 120ms - worst-case click latency is that
budget - and the derived topology (~292k weights to ~29k) makes the
throughput this costs far cheaper than when that note was written.
Also halved the panel redraw rate to 2.5 Hz: ChartRedraw repaints the
whole chart, so its cost scales with accumulated arrows, and 5 Hz was
the larger half of the stutter. Era-end still force-refreshes.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 09:05:58 -04:00
|
|
|
//--- DELIBERATELY NOT AN INPUT. Development diagnostics: dumps the training internals that used to sit on
|
|
|
|
|
//--- the on-chart panel (plateau-ladder stage, eras-since-best, the deploy gate, selection internals) into
|
|
|
|
|
//--- the Experts journal instead, where they cost the user nothing. This is a commercial product - the
|
|
|
|
|
//--- default panel has to read like a product, not like a training console, so anything a buyer cannot act
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- on belongs in a log. Flip to true and recompile when diagnosing a training run.
|
fix(ui): unique chart tag, product-grade panel, responsive under load
Three separate reports from one deploy.
1. CONV, LSTM and HYBRID all came back tagged [4109]. The weights
fingerprint omits the topology type on purpose - the file path already
separates it (State\CONV\ vs State\LSTM\ vs State\HYB\) and hashing a
value that is constant within a folder buys nothing while re-keying
every trained model into a forced retrain. So the files were never at
risk, but the tag could not do its one job. Prefixing the short id
makes it unique on the display side only; the hex half still greps
straight to the .nnw inside the folder the prefix names.
2. The default panel read like a training console. Six lines down to
three, each answering a question an owner actually has. The deploy
internals (best score, eras-since-best, ladder stage) were developer
diagnostics describing a recall floor that no longer decides anything,
and were already in the era-end journal line. In-sample accuracy left
the panel too: it grades the model on bars it trained on, so it always
flatters, and showing it beside the honest number invites reading the
wrong one. New compile-time DebuggingMode constant - deliberately not
an input - carries the IS/OOS pair and the resolved model path into
the journal instead. No extra Inputs row, no extra Market description
line, no user-reachable firehose.
3. Panel drag and buttons stuttered under training load, exactly as the
2026-07-26 note raising the chunk budget to 200ms warned they might.
Backed off to the documented 120ms - worst-case click latency is that
budget - and the derived topology (~292k weights to ~29k) makes the
throughput this costs far cheaper than when that note was written.
Also halved the panel redraw rate to 2.5 Hz: ChartRedraw repaints the
whole chart, so its cost scales with accumulated arrows, and 5 Hz was
the larger half of the stutter. Era-end still force-refreshes.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 09:05:58 -04:00
|
|
|
const bool DebuggingMode = false;
|
feat(nn): derive dense depth, train on all history, pin the shape in .cfg
Completes the derived-topology work. Three inputs removed.
AIType loses its depth suffix - AI_MLP/AI_CONV/AI_LSTM/AI_HYBRID, five
entries instead of eight. Depth is now derived from the two endpoints
the taper already has to connect (derived first-layer width, output-tied
final width) at a 2x per-layer compression target, clamped [2..5].
Asking a user to pick a layer count while the code derives the widths
those layers taper between was asking for half a decision: at 64 units
tapering to 12, four layers compress by 1.4x per step and five by 1.3x,
so the extra depth bought no abstraction. On the shipping H1/10y default
the derivation lands on 3 layers - the depth that actually won Run 2.
StudyPeriods removed. There is no case for training on less data than
the broker provides at a ~6% directional base rate; the honest
generalization read comes from the OOS holdout, not from withholding
history. Training now starts at the earliest available bar, floored by
MinTrainYear, which answers a different question (excluding dubious
pre-history) and stays.
That required closing the hazard the old code documented: the capacity
budget now MEASURES the symbol's real bar count, and a topology derived
from a measurement would widen as history downloads. Both ends are now
pinned. Every derived value left the weights-filename fingerprint -
keying a filename on a measured quantity means the EA looks for a file
that does not exist, starts from era 0 and orphans a trained model,
silently, because a missing cache is the normal first-run state. The
shape lives in the .cfg instead, where LoadAndCompare now ADOPTS the
four derived fields rather than diffing them; a mismatch there would
discard a fully-trained model over nothing the user did. Two fields
appended to the .cfg for the conv/LSTM stages, length-guarded on read
because FileReadInteger past EOF returns 0 with no error.
ForceHiddenLayers, a compile-time constant like DebuggingMode, pins
depth for diagnostic comparisons. It joins the fingerprint only when
non-zero, so forced depths get their own files - sequential comparisons
only, not simultaneous from one .ex5.
Derived shape, H1/10y defaults (21 features x 20 bars): first layer 64,
3 dense, 8 conv filters, 16 LSTM units. The LSTM block halves from
~58k to ~28k weights.
Both builds compile 0 errors, 0 warnings. Re-keys existing models.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 10:05:40 -04:00
|
|
|
//--- ALSO NOT AN INPUT, and for a stronger reason than DebuggingMode. Pins the dense-taper depth instead
|
|
|
|
|
//--- of deriving it (ComputeHiddenLayerCount), purely so a depth comparison can still be run while
|
|
|
|
|
//--- working on the EA. 0 = derived, which is the only value that should ever ship. A user who picks a
|
|
|
|
|
//--- depth is contradicting the first-layer width and the taper the code derived around it - that
|
|
|
|
|
//--- contradiction is exactly what the MLP_3L/MLP_4L presets used to allow.
|
|
|
|
|
//--- NOTE the limitation: this is compile-time, and it feeds the weights-filename fingerprint only when
|
|
|
|
|
//--- non-zero, so two forced depths get their own model files but cannot run SIMULTANEOUSLY from one
|
|
|
|
|
//--- .ex5. Depth comparisons are sequential unless you deploy two separately-compiled builds.
|
|
|
|
|
const int ForceHiddenLayers = 0;
|
2026-07-22 13:33:56 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// MONEY MANAGEMENT
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string MM_Settings = "Money Management"; // Money Management
|
|
|
|
|
input MONEY_MANAGEMENT_STRATEGY MM_STRATEGY = FIXED_RISK; // MM strategy
|
|
|
|
|
input MONEY_RISK_PERCENT_PRESET Money_Risk_Percent = RISK_PCT_1; // Risk % of balance per trade
|
|
|
|
|
input double Money_FixLot_Lots = 0.01; // Fixed lot size [0.01-10]
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
// TRADE MANAGEMENT (entry / stop / target / trailing / exit)
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string Entry_Settings = "Trade Management"; // Trade Management
|
|
|
|
|
input TRADING_DIRECTION tradingdirection = BOTH; // Trade direction
|
feat: entry/SL/TP stop being inputs - the barrier geometry is measured
Three enums left the Inputs tab. They were three things a user had to pick and,
in the tester, three more axes for a genetic optimization to overfit.
Entry_Multiplier is pinned to MARKET. Its pending modes place the entry at a
LEVEL while the rest of the pipeline measures from the bar open - the exact
mismatch that manufactured the +0.097 R "retail fade" result later retracted as
a fill artifact. This codebase's fill model cannot honestly simulate a pending
entry, so it is no longer offered.
SL_Mode/TP_Mode become a STARTING pair. ReportBarrierGeometryScan now ADOPTS its
winner instead of printing "set SL_Mode/TP_Mode to X and retrain":
- only when it clears the family-wise gate from 04ee2e1 (beat the null of the
MAXIMUM, not merely the incumbent). This is why that gate had to land first:
without it, removing the inputs would hand a noise-picked geometry direct
control over the training target with no human in the loop - strictly worse
than the input it replaced. On SP500 H1 today it does NOT clear (p=0.1463),
so 2:6 is what you get - now chosen by measurement rather than assumed.
- only at m_eraCount == 0. Relabelling a partly-trained net moves the target
out from under weights already fitted to the old one.
THE GEOMETRY LEFT THE WEIGHTS-FILENAME HASH, because it is now measured. Same
rule that moved the horizon and the derived topology values out: a filename
keyed on a measured quantity changes the moment the measurement does - a few
more bars shift which pairing wins - and the EA then looks for a file that does
not exist, starts from era 0 and orphans a trained model silently. It is PINNED
IN THE .cfg instead: appended at the end (the only backward-safe change),
length-guarded like the 2026-07-30 derived pair, and ADOPTED on load rather than
compared, so a trained model keeps the barriers it actually learned and never
re-measures.
Two traps closed while wiring it, neither of which announces itself:
- m_barrierHorizonResolved latches the horizon ONCE PER PROCESS. Adopting 2:8
(wants ~192 bars) after it settled for 2:6 (128) would label the new target
against the old ceiling - the truncation fixed in 168422f, where every model
learned "target within 128 bars" while the EA holds to SL/TP. It lands in
Neutral, not in the timeout counter watching for it. Unlatched on adoption,
along with the label cache the old barriers filled.
- the .cfg adopt runs at init, before the horizon latches and before any label
is computed, so a resumed model has its pinned pair in place first. Verified,
not assumed.
FORCES A FULL RETRAIN: the fingerprint change orphans every existing .nnw.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 09:39:30 -04:00
|
|
|
//--- ENTRY / STOP / TARGET ARE NO LONGER INPUTS (2026-08-07). They were three enums the user had to pick,
|
|
|
|
|
//--- and in the tester they were three more axes for a genetic optimization to overfit. The barrier
|
|
|
|
|
//--- geometry is now MEASURED (ReportBarrierGeometryScan picks the SL:TP pairing that carries the most
|
|
|
|
|
//--- entry-time information about its own outcome, and only adopts it when it clears a family-wise
|
|
|
|
|
//--- significance gate - otherwise these defaults stand). Kept as named constants rather than deleted so
|
|
|
|
|
//--- every existing reference still reads the same, and so the fallback is stated in one place.
|
|
|
|
|
//---
|
|
|
|
|
//--- Entry is pinned to MARKET deliberately. The pending-order modes place the entry at a LEVEL while the
|
|
|
|
|
//--- rest of the pipeline measures from the bar open, which is precisely the mismatch that manufactured
|
|
|
|
|
//--- the +0.097 R "retail fade" result later retracted as a fill artifact - a pending entry cannot be
|
|
|
|
|
//--- honestly simulated by this codebase's own fill model, so it is not offered.
|
|
|
|
|
const ENTRY_MULTIPLIER Entry_Multiplier = MARKET; // Entry type/offset (fixed - see above)
|
|
|
|
|
//--- STARTING geometry only. The scan may replace this pair at era 0 on a fresh model; a model that has
|
|
|
|
|
//--- already been trained reads its pinned pair back out of the .cfg and never re-measures, so the labels
|
|
|
|
|
//--- a run started with are the labels it finishes with.
|
2026-08-09 14:51:59 -04:00
|
|
|
//---
|
|
|
|
|
//--- MIN REWARD:RISK IS GONE (2026-08-09). It was the last place a GUESS could override a MEASUREMENT.
|
|
|
|
|
//--- The barrier geometry is derived from the instrument's own excursion distribution - stop at q75 of
|
|
|
|
|
//--- adverse travel, target at q50 of favourable - and then a 1:2 floor was applied on top, raising the
|
|
|
|
|
//--- target to whatever twice the stop happened to be. On SP500 H1 that turned a reachable target into
|
|
|
|
|
//--- 6.66*ATR, which only 3.3% of bars reach inside the horizon: the label became "almost never a win",
|
|
|
|
|
//--- and the model was trained to predict an event that essentially does not occur.
|
|
|
|
|
//---
|
|
|
|
|
//--- The ratio never bought anything it was believed to buy. A reward:risk floor does not create
|
|
|
|
|
//--- expectancy - it trades hit rate for payoff at a fixed break-even (see the barrier-geometry log
|
|
|
|
|
//--- line, which prints that break-even next to the ranking precisely to make this visible), and this
|
|
|
|
|
//--- project has already MEASURED that exit shape moves payoff without moving expectancy at all. What
|
|
|
|
|
//--- it did buy was two outages: four consecutive Market validation rejections for "no trading
|
|
|
|
|
//--- operations" when it rejected 100% of setups, and the label corruption above.
|
|
|
|
|
//---
|
|
|
|
|
//--- Risk is controlled where risk is actually controlled - the per-trade account risk percentage and
|
|
|
|
|
//--- CRiskBudget's daily/total drawdown enforcement - not by a ratio filter at the door.
|
feat: entry/SL/TP stop being inputs - the barrier geometry is measured
Three enums left the Inputs tab. They were three things a user had to pick and,
in the tester, three more axes for a genetic optimization to overfit.
Entry_Multiplier is pinned to MARKET. Its pending modes place the entry at a
LEVEL while the rest of the pipeline measures from the bar open - the exact
mismatch that manufactured the +0.097 R "retail fade" result later retracted as
a fill artifact. This codebase's fill model cannot honestly simulate a pending
entry, so it is no longer offered.
SL_Mode/TP_Mode become a STARTING pair. ReportBarrierGeometryScan now ADOPTS its
winner instead of printing "set SL_Mode/TP_Mode to X and retrain":
- only when it clears the family-wise gate from 04ee2e1 (beat the null of the
MAXIMUM, not merely the incumbent). This is why that gate had to land first:
without it, removing the inputs would hand a noise-picked geometry direct
control over the training target with no human in the loop - strictly worse
than the input it replaced. On SP500 H1 today it does NOT clear (p=0.1463),
so 2:6 is what you get - now chosen by measurement rather than assumed.
- only at m_eraCount == 0. Relabelling a partly-trained net moves the target
out from under weights already fitted to the old one.
THE GEOMETRY LEFT THE WEIGHTS-FILENAME HASH, because it is now measured. Same
rule that moved the horizon and the derived topology values out: a filename
keyed on a measured quantity changes the moment the measurement does - a few
more bars shift which pairing wins - and the EA then looks for a file that does
not exist, starts from era 0 and orphans a trained model silently. It is PINNED
IN THE .cfg instead: appended at the end (the only backward-safe change),
length-guarded like the 2026-07-30 derived pair, and ADOPTED on load rather than
compared, so a trained model keeps the barriers it actually learned and never
re-measures.
Two traps closed while wiring it, neither of which announces itself:
- m_barrierHorizonResolved latches the horizon ONCE PER PROCESS. Adopting 2:8
(wants ~192 bars) after it settled for 2:6 (128) would label the new target
against the old ceiling - the truncation fixed in 168422f, where every model
learned "target within 128 bars" while the EA holds to SL/TP. It lands in
Neutral, not in the timeout counter watching for it. Unlatched on adoption,
along with the label cache the old barriers filled.
- the .cfg adopt runs at init, before the horizon latches and before any label
is computed, so a resumed model has its pinned pair in place first. Verified,
not assumed.
FORCES A FULL RETRAIN: the fingerprint change orphans every existing .nnw.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 09:39:30 -04:00
|
|
|
const STOP_LOSS_MODE SL_Mode = SL_ATR_x2; // Stop-loss mode (measured - see above)
|
|
|
|
|
const TAKE_PROFIT_MODE TP_Mode = TP_ATR_x6; // Take-profit mode (measured - see above)
|
2026-07-22 13:33:56 -04:00
|
|
|
input TRAILING_STRATEGY TrailingStrategy = TRAILING_STRATEGY_NONE; // Trailing stop
|
|
|
|
|
input BARS_EXPIRATION Signal_Expiration = BARS_X3; // Pending order expiry (bars)
|
|
|
|
|
input CONFIDENCE_SOURCE Confidence_Source = CONF_AI; // AI confidence source (SL/TP/trail/exit/MM)
|
2026-07-26 17:27:51 -04:00
|
|
|
//--- UNIFIED conviction gates - ONE pair of thresholds governing BOTH engines, classic and AI. There
|
|
|
|
|
//--- used to be a second, AI-only pair in the Neural Network section (Min AI confidence / Min AI exit
|
|
|
|
|
//--- confidence) duplicating these: four inputs for what is really two decisions, where a trader could
|
|
|
|
|
//--- set the vote gate and still be silently overruled by the AI floor (or the reverse). Merged here.
|
|
|
|
|
//--- Everything is expressed on the same 0-100 conviction scale: a classic filter contributes its
|
|
|
|
|
//--- pattern weight (10-100), an AI signal contributes its confidence tier (80-100), and
|
|
|
|
|
//--- CExpertSignalCustom::Direction() averages the filters that voted before
|
|
|
|
|
//--- CheckOpenPosition/CheckClosePosition threshold that average.
|
2026-07-26 18:33:12 -04:00
|
|
|
//--- Open - aggregate conviction required to ENTER, and NOTHING else. It has exactly one meaning for
|
|
|
|
|
//--- both engines: the averaged vote across the filters that voted must reach it.
|
|
|
|
|
//--- It used to do two further jobs on the AI side - an entry floor on the winning softmax
|
|
|
|
|
//--- probability, and the base the 4 AI confidence tiers were quartiled from - which put one
|
|
|
|
|
//--- number on two incompatible scales. A 3-class argmax winner is arithmetically >= 1/3, so
|
|
|
|
|
//--- as a floor every setting from 0 to 33 gated precisely nothing, while every setting above
|
|
|
|
|
//--- that ALSO silently moved the tier boundaries. Both jobs are gone. The AI now expresses
|
|
|
|
|
//--- confidence the way a classic signal does - as the WEIGHT of the vote it casts, 25/50/75/
|
|
|
|
|
//--- 100 across its four tiers, quartiled from the head's own structural floor (1/3 for the
|
|
|
|
|
//--- 3-class softmax, 0.5 for the regression head - see CExpertSignalAIBase::ConfidenceTier).
|
|
|
|
|
//--- So this input now reads, for the AI voting alone: 25 = trade any directional call,
|
|
|
|
|
//--- 50 = tier 1 and up, 75 = tier 2 and up, 100 = only near-certain calls. A weak AI call is
|
|
|
|
|
//--- no longer blocked inside the AI - it votes weakly and is filtered here, exactly like a
|
|
|
|
|
//--- weight-10 classic confirmation.
|
|
|
|
|
//--- NOTE in a hybrid setup this is an AVERAGE: a tier-3 AI vote of 100 alongside two
|
|
|
|
|
//--- weight-10 classic confirmations averages to 40, not 100. Raising this input while several
|
|
|
|
|
//--- low-weight classic signals are enabled suppresses strong AI calls by dilution - that is
|
|
|
|
|
//--- inherent to averaging, and it is the same arithmetic the classic-only path has always had.
|
|
|
|
|
//--- Close - OPPOSITE conviction required to EXIT. It drives BOTH exit routes, at the same conviction:
|
|
|
|
|
//--- the averaged rule-based vote, and the AI early exit (how strongly the AI must have flipped
|
|
|
|
|
//--- AGAINST an open position before that alone closes it). There is deliberately no separate
|
|
|
|
|
//--- "Early AI exit" switch any more - it was a third input for what these two routes already
|
|
|
|
|
//--- express, and it could be left off while Close was set, silently discarding the exit the
|
|
|
|
|
//--- trader had just asked for. The two routes are NOT redundant with each other and both are
|
|
|
|
|
//--- needed: the AI's normal vote is one-shot (LongCondition/ShortCondition consume the
|
|
|
|
|
//--- m_lastNonNeutralSignal alternation gate when they fire) and is then AVERAGED with every
|
|
|
|
|
//--- other filter, so an AI reversal that gets diluted below Close on the bar it happens is
|
|
|
|
|
//--- consumed and never re-offered, leaving the position open indefinitely. The early-exit
|
|
|
|
|
//--- route reads the AI's LIVE signed confidence every bar, undiluted, and so still fires.
|
|
|
|
|
//--- Set Close = Disabled to switch off vote-driven exits entirely (SL/TP/trailing only) -
|
|
|
|
|
//--- that turns off both routes at once, since 101 is unreachable on either scale. See
|
|
|
|
|
//--- VOTE_CLOSE_PRESETS in Enumerations\InputEnums.mqh.
|
2026-07-26 17:27:51 -04:00
|
|
|
//--- Close defaults ABOVE Open deliberately: a position is an existing commitment with real cost to
|
|
|
|
|
//--- abandon, so reversing out of one should demand more conviction than opening it did, and a signal
|
|
|
|
|
//--- hovering either side of the entry gate must not be able to churn a position open and shut. Both
|
|
|
|
|
//--- were once hardcoded to 10/10 - one value for BOTH directions of the decision, pinned at the LOWEST
|
|
|
|
|
//--- weight any pattern can carry - so with MA/RSI Pattern_0 (weight 10) firing on nearly every bar on
|
|
|
|
|
//--- whichever side of the MA price sits, one cross flipped the average from +10 to -10 and closed the
|
|
|
|
|
//--- position on the very next bar. The stock MQL5 wizard makes the same asymmetric choice, 50 to open
|
|
|
|
|
//--- against 100 to close.
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
input PERCENTAGE_PRESETS Min_Vote_Open = PCT_20; // Min vote to open - AI + classic
|
2026-07-26 18:48:34 -04:00
|
|
|
input VOTE_CLOSE_PRESETS Min_Vote_Close = VOTE_CLOSE_DISABLED; // Min opposite vote to close - AI + classic
|
2026-07-22 13:33:56 -04:00
|
|
|
//==================================================================================================
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
// CLASSIC SIGNALS (rule-based MA/RSI votes - trade alongside or instead of the neural network)
|
2026-07-22 17:17:23 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
input string Classic_Settings = "Classic Signals"; // Classic Signals
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
//--- EnableMA/EnableRSI default depends on the build (see AIType's declaration comment for the full
|
|
|
|
|
//--- rationale): ON for a Market submission build (WARRIOR_MARKET_BUILD defined) so a fresh install
|
|
|
|
|
//--- trades immediately with no AI warm-up, OFF for the private/live build, which runs AI-only by
|
|
|
|
|
//--- default. Either way this is only a compile-time DEFAULT - still a normal input, changeable per-run
|
2026-07-22 22:51:04 -04:00
|
|
|
//--- from the Inputs tab without recompiling.
|
2026-08-13 16:31:15 -04:00
|
|
|
//--- PRIVATE-BUILD DEFAULTS CHANGED 2026-08-13 (user request, meta-pooling campaign): all four
|
|
|
|
|
//--- classic families default ON in the private build - they are the META chart's candidate sources
|
|
|
|
|
//--- for the on-chart ladder sweep (BuildCorpusBySweep), and the drop-on-chart workflow must need no
|
|
|
|
|
//--- Inputs-tab edits. Market build unchanged: MA/RSI on (trades out of the box), MACD/Ichimoku off
|
|
|
|
|
//--- (additive to the shipped strategy).
|
2026-07-22 22:51:04 -04:00
|
|
|
#ifdef WARRIOR_MARKET_BUILD
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
input bool EnableMA = true; // MA classic vote
|
2026-07-22 22:51:04 -04:00
|
|
|
#else
|
2026-08-13 16:31:15 -04:00
|
|
|
input bool EnableMA = true; // MA classic vote
|
2026-07-22 22:51:04 -04:00
|
|
|
#endif
|
|
|
|
|
#ifdef WARRIOR_MARKET_BUILD
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
input bool EnableRSI = true; // RSI classic vote
|
2026-07-22 22:51:04 -04:00
|
|
|
#else
|
2026-08-13 16:31:15 -04:00
|
|
|
input bool EnableRSI = true; // RSI classic vote
|
2026-07-22 22:51:04 -04:00
|
|
|
#endif
|
2026-08-13 16:31:15 -04:00
|
|
|
#ifdef WARRIOR_MARKET_BUILD
|
2026-07-26 18:33:12 -04:00
|
|
|
input bool EnableMACD = false; // MACD classic vote
|
|
|
|
|
input bool EnableIchimoku = false; // Ichimoku classic vote
|
2026-08-13 16:31:15 -04:00
|
|
|
#else
|
|
|
|
|
input bool EnableMACD = true; // MACD classic vote
|
|
|
|
|
input bool EnableIchimoku = true; // Ichimoku classic vote
|
|
|
|
|
#endif
|
2026-07-26 18:33:12 -04:00
|
|
|
//--- Indicator parameters below are SHARED: they define the classic votes above AND seed the matching AI
|
|
|
|
|
//--- input features (AI Input Features section) as their starting period, which Auto-tune indicators then
|
|
|
|
|
//--- searches from. Set once here, used by whichever consumer(s) are enabled.
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
input MA_PERIOD_PRESETS PeriodMA = MA_PERIOD_50; // MA period
|
feat(ai): triple-barrier labels replace exact-pivot ZigZag targets
The 31:1 class imbalance was self-inflicted by the TARGET, not a property
of the market. Labelling only the exact bar where a ZigZag pivot confirms
gave Buy 1164 / Sell 1164 / Neutral 35841, and every correction mechanism
this codebase accumulated sits downstream of that one choice: the
logit-adjusted loss and its range cap, the prior EMA, the +-3.0 output-bias
seed, balanced-accuracy-then-precision selection with its coverage floor,
the recall floor and its catch-22, the alternation gate, NMS, and the four
oversampling designs that collapsed before them.
The reference this engine is built on (references/neuronetworksbook.pdf
ch. 3.1/3.3) also uses ZigZag, but targets the DIRECTION TO THE NEXT
EXTREMUM on every bar - ~50/50 by construction, with no imbalance to
correct at all. It never had this problem because it never asked "is this
the pivot bar".
Labels are now the triple barrier (Lopez de Prado ch. 3), using the EA's
OWN SL_Mode/TP_Mode: does a trade opened at this bar's close reach its
target before its stop, within a horizon. Buy = long resolves, Sell =
short resolves, Neutral = neither. Consequences:
- dir-precision in the era line stops being a proxy and becomes the win
rate of the strategy under its own exit rules.
- Expected balance ~25/25/50 at the shipped 1:3 (gambler's ruin), i.e.
~2:1 instead of 31:1. Measured and logged at the end of the prebuild.
- Spread is charged on both legs, so it is a NET win rate.
- Intrabar ambiguity resolves to the STOP. OHLC cannot order two touches
inside one bar and the optimistic reading is how a backtested edge
becomes a live loss.
ZigZag stays as input features (EnableSwingContext) and now also supplies
the vertical barrier: the horizon is the median confirmed leg length,
snapped to a coarse ladder. Derived, not configured, and deliberately kept
out of the filename fingerprint - a filename keyed on a measured quantity
orphans a trained model the moment the measurement moves.
Removed, because the premise died with the old target:
- the alternation gate. Correct for pivot labels (a ZigZag cannot emit two
same-type pivots in a row, so a repeat was provably a false fire), and
wrong for barrier labels, which answer each bar independently. It also
took its worst consequence with it: a one-sided model previously got ONE
trade per backtest, a hard blocker on marketplace validation.
- SignalClusterWindow now defaults off - it de-duplicated repeats that are
now real trades. Kept as an opt-in display control.
- LABEL_WINDOW_BARS, the pivot-widening pass, ConfirmedZigZagLabel.
- the era-0 output-bias seed now needs a genuinely dominant class (0.70)
rather than 0.40; at ~50% Neutral a +-3.0 seed is a distortion, not a
correction.
Also fixed, both found while wiring the above:
1. RefreshConvergedSignal sized its buffers from a date delta
(Bars(sym, period, dtStudied, TimeCurrent())). dtStudied is a training
watermark; in the tester it is loaded from a live-chart save AHEAD of
the simulated date, so the interval inverted, Bars() returned ~0, and
the buffer came out at exactly m_historyBars - deep enough for the OHLC
window and far too shallow for the Donchian-50 / 20-bar-return / SMA
extension behind it. Inference silently computed DIFFERENT features
from the ones training learned on, live as well as in the tester. Now
sized from what the feature builder actually needs.
2. The barrier horizon is resolved on the deployed path too. A deployed
model never enters Train(), so it never reached the prebuild, and
OnlineLearnStep reads the horizon as its confirmation delay - left at
the fallback it would have backpropped bars whose barriers had not
resolved. Silent lookahead in the one place that writes to a live model.
SL_Mode/TP_Mode join the weights fingerprint: they define the labels now,
so a model trained at 1:3 must never be silently reused at 1:1. This
re-keys every pre-existing model by design - none were trained on this task.
Inference census extended with the vote gate. LongCondition/ShortCondition
open with a readiness check the refresh counters never see; in the tester it
reduces to "the seeded _optcache.nnw must have LOADED", and if it did not,
every vote is hard-zeroed while the model still answers Buy. The old three
counters would have read that as "the model says Neutral" - false, and a
completely different fix. This is the leading candidate for the
zero-direction backtest and the census can now name it in one run.
Both builds compile 0 errors / 0 warnings. Forces a full retrain.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 20:39:49 -04:00
|
|
|
input MA_TYPE_PRESETS MA_Type = MA_TYPE_SMA; // MA type (SMA/EMA/.../T3/Kalman)
|
2026-07-22 22:51:04 -04:00
|
|
|
input RSI_PERIOD_PRESETS PeriodRSI = RSI_PERIOD_14; // RSI period
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- MACD AND ICHIMOKU PERIODS ARE NO LONGER INPUTS (2026-08-01). Six dropdowns, pinned here at the
|
|
|
|
|
//--- textbook values every reference uses (12/26/9 and 9/26/52), for three reasons:
|
|
|
|
|
//--- 1. They were six of the largest contributors to the Inputs tab, for indicators that both ship
|
|
|
|
|
//--- DISABLED. Rows a user must scroll past to reach the AI settings are a real cost.
|
|
|
|
|
//--- 2. As optimizer inputs they are an overfitting surface. A genetic sweep across 6 period
|
|
|
|
|
//--- dimensions on one symbol's history will always find a combination that looks excellent and
|
|
|
|
|
//--- generalizes to nothing - and it costs nothing to discover, which is what makes it dangerous.
|
|
|
|
|
//--- 3. They are the SEED for the AI's own auto-tuner (AutoTuneIndicators), which searches from these
|
|
|
|
|
//--- values against a held-out objective. That search is the supported way to move them: it is
|
|
|
|
|
//--- validated, it is per-model, and it cannot silently overfit the way a raw optimizer pass can.
|
|
|
|
|
//--- Left as named constants rather than deleted because they are still read in both roles (classic
|
|
|
|
|
//--- vote periods AND auto-tune starting points), and because the classic textbook values are the
|
|
|
|
|
//--- correct fixed answer for a vote that exists mainly to satisfy marketplace validation.
|
|
|
|
|
const MACD_FAST_PRESETS MACD_PeriodFast = MACD_FAST_12;
|
|
|
|
|
const MACD_SLOW_PRESETS MACD_PeriodSlow = MACD_SLOW_26;
|
|
|
|
|
const MACD_SIGNAL_PRESETS MACD_PeriodSignal = MACD_SIGNAL_9;
|
|
|
|
|
const ICHIMOKU_TENKAN_PRESETS Ichimoku_PeriodTenkan = ICHI_TENKAN_9;
|
|
|
|
|
const ICHIMOKU_KIJUN_PRESETS Ichimoku_PeriodKijun = ICHI_KIJUN_26;
|
|
|
|
|
const ICHIMOKU_SENKOU_PRESETS Ichimoku_PeriodSenkou = ICHI_SENKOU_52;
|
2026-07-22 13:33:56 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// NEURAL NETWORK (training)
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string NNetworks_Settings = "Neural Network"; // Neural Network
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
//--- AIType default depends on the build, via the same WARRIOR_MARKET_BUILD compile-time flag that
|
|
|
|
|
//--- strips the DLL import block for Market submissions (see Warrior_EA.mq5's top-of-file comment) - not
|
|
|
|
|
//--- an input value itself (that can't be set programmatically), only which default the Inputs tab
|
|
|
|
|
//--- starts on:
|
2026-07-22 22:51:04 -04:00
|
|
|
//--- - WARRIOR_MARKET_BUILD defined (Market submission): OFF - a fresh install trades from Classic
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00
|
|
|
//--- Signals (MA/RSI) out of the box with no AI warm-up, satisfying MQL5's automated check for live
|
|
|
|
|
//--- trade activity within its test window.
|
|
|
|
|
//--- - Not defined (private/live build): MLP - this build runs AI-only from the start, with Classic
|
|
|
|
|
//--- Signals defaulting off too (see EnableMA/EnableRSI), so no per-run manual input changes are
|
|
|
|
|
//--- needed switching between preparing a submission and running the real thing.
|
|
|
|
|
//--- Either way, still a normal input - freely changeable per-run from the Inputs tab.
|
2026-07-22 22:51:04 -04:00
|
|
|
#ifdef WARRIOR_MARKET_BUILD
|
2026-07-28 12:02:58 -04:00
|
|
|
input AI_CHOICE AIType = AI_NONE; // AI architecture preset (or Disabled)
|
2026-07-22 22:51:04 -04:00
|
|
|
#else
|
2026-08-13 16:31:15 -04:00
|
|
|
//--- META since 2026-08-13 (was HYBRID): the private build's active campaign is the meta-labeling
|
|
|
|
|
//--- pool - drop a chart on any symbol and it sweeps candidates, labels, trains and exports with no
|
|
|
|
|
//--- Inputs-tab edits. Direction models remain selectable per-run as always.
|
|
|
|
|
input AI_CHOICE AIType = AI_META; // AI architecture preset (or Disabled)
|
2026-07-22 22:51:04 -04:00
|
|
|
#endif
|
2026-07-27 22:08:55 -04:00
|
|
|
//--- SGD or ADAM weight update (honored by PAI/CONV/LSTM/HYBRID). SGD rate/momentum are AI\Network.mqh inputs.
|
2026-07-29 00:03:54 -04:00
|
|
|
//--- A third "DFA" option was briefly the default (2026-07-28) and has been removed - it was a
|
|
|
|
|
//--- deterministic index-parity sign flip on the gradient, i.e. permanent gradient ASCENT on half of
|
|
|
|
|
//--- every weight tensor, and its backward pass was structurally incompatible with the OpenCL/DirectML
|
|
|
|
|
//--- neuron model. See ENUM_OPTIMIZATION's comment in AI\Network.mqh.
|
|
|
|
|
input ENUM_OPTIMIZATION TrainingOptimizer = ADAM; // Weight optimizer
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- OUTPUT TYPE IS NO LONGER AN INPUT (2026-08-01). The regression head (1 tanh output) was an option
|
|
|
|
|
//--- that never made sense for the question this system asks. Since the triple-barrier relabel the
|
|
|
|
|
//--- target is explicitly an EVENT - "does a trade opened here reach its target before its stop" - and
|
|
|
|
|
//--- the right output for an event is its probability, which is what the 3-class softmax head produces.
|
|
|
|
|
//--- A regression head would have to predict a continuous quantity that the label does not even contain,
|
|
|
|
|
//--- and every downstream consumer already speaks probability: the confidence tiers quartile the softmax
|
|
|
|
|
//--- winner, dir-precision is a win rate over called bars, and the class priors calibrate a distribution.
|
|
|
|
|
//--- The regression path is still IMPLEMENTED throughout (m_outputNeuronsCount == 1 branches, the 0.50
|
|
|
|
|
//--- magnitude cutoff in DoubleToSignal) and is left in place deliberately: it costs nothing dormant and
|
|
|
|
|
//--- removing it would touch every scoring path at once for no gain. It is simply no longer selectable.
|
|
|
|
|
const OUTPUT_NEURONS_COUNT OutputNeuronsCount = OUTPUT_CLASSIFICATION;
|
refactor(ai): derive the first dense layer's width instead of asking for it
InitialNeurons was an input whose only defensible value depends on two
things the user cannot see when picking from a dropdown: how wide the input
vector ended up after feature selection, and how much in-sample data the
study period actually yields. Left to a hand-picked constant it was badly
wrong - 500 units against a 420-wide input is 210,500 weights, 72% of a
292,583-weight model, against ~36,500 training bars of which only ~2,236
are directional. That is 6.6 weights per training bar, and it EXPANDS a set
of highly correlated inputs rather than compressing them.
The symptom was already in the logs and had been read as a depth problem:
the shallowest topology consistently beat the deepest (perceptron 52.7%
balanced, hybrid 41.3%). Over-parameterization predicts that ordering just
as well as covariate shift does, and only one of the two had been addressed.
ComputeFirstLayerWidth() budgets roughly one first-layer weight per
in-sample bar. Measured across the configurations in use:
M15 10y -> 256 units, 129,071 weights, 0.73 per bar
H1 10y -> 64 units, 28,727 weights, 0.65 per bar
H4 10y -> 16 units, 7,559 weights, 0.68 per bar
Two design points that matter:
- It estimates in-sample bars from the STUDY PERIOD and timeframe, not
from Bars(). What is downloaded grows over a terminal's lifetime, and a
topology that widened as history filled in would re-key its own weights
file and discard a trained model.
- The result is snapped down to a coarse power-of-two ladder, so the
estimate would have to be wrong by ~2x to change the answer.
Every field it reads is already part of the weights-filename fingerprint,
so the derived value needs no fingerprint entry of its own. The public
setter is removed - it could only have been called after construction, and
would either be ignored or silently re-key the model mid-run.
Where the data cannot support even the floor (D1 over 10 years is under
2,000 bars) it now says so and names the fixes, rather than quietly
training a model with more weights than examples.
The DB config fingerprint drops the term too, which re-keys existing
pattern databases once - correct, since a model an order of magnitude
smaller should not inherit the old one's win-rate history.
Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 13:01:16 -04:00
|
|
|
//--- No "first layer neurons" input any more. Its only defensible value depends on two things the user
|
|
|
|
|
//--- cannot see - the input-vector width after feature selection, and how much in-sample data the study
|
|
|
|
|
//--- period yields - so it is derived at topology-build time instead. See
|
|
|
|
|
//--- CExpertSignalAIBase::ComputeFirstLayerWidth(). The old default (500) was ~8 parameters per training
|
|
|
|
|
//--- sample and expanded a 420-wide correlated input rather than compressing it.
|
2026-07-30 09:22:11 -04:00
|
|
|
//--- No "LSTM hidden size" or "CONV filter count" inputs either, removed 2026-07-30 for exactly the
|
|
|
|
|
//--- reason above: both defaulted to a fixed constant (32 units, 16 filters) chosen without reference to
|
|
|
|
|
//--- the input they sit on, which is the one thing that decides whether either number is sane.
|
|
|
|
|
//--- The conv layer is a per-bar projection (window = step = one bar's features), so 16 filters
|
|
|
|
|
//--- COMPRESSED a 50-feature configuration but EXPANDED a minimal 4-feature one 4x - adding parameters
|
|
|
|
|
//--- below every learnable layer without adding information. The LSTM block is worse: its weight count
|
|
|
|
|
//--- is 4*H*(H+inputs+1), so 32 units against a 540-wide input is ~73k weights, more than double the
|
|
|
|
|
//--- entire derived dense taper it feeds, and it was the one stage the capacity budget never covered.
|
|
|
|
|
//--- Both are now derived from the per-bar feature count and the same one-weight-per-training-bar budget
|
|
|
|
|
//--- the first layer uses. See ComputeConvFilterCount()/ComputeLstmHiddenSize().
|
fix(ai): drop the conv pooling stage - it reduced across filters, not time
FeedForwardConv emits POSITION-MAJOR output, matrix_o[out + window_out * i],
so one bar's window_out filter responses are contiguous and consecutive bars
sit window_out apart. Both pooling implementations (FeedForwardProof and
CPU_FeedForwardProof) slide FLAT over that buffer - pos = i * step, reducing
`window` CONSECUTIVE elements. On a position-major layout those neighbours
are different FILTERS of the same bar, never one filter across time.
At the shipped 3/2 the pool computed max(bar0_f0, bar0_f1, bar0_f2), then
max(bar0_f2, bar0_f3, bar0_f4), with every 8th window straddling a bar
boundary. So it collapsed unrelated feature detectors into whichever fired
hardest, passed gradient to that winner only, and halved the feature map
while doing it - all below every learnable layer, where nothing above can
recover it. The removed inputs' own labels ("3 Bars") show time-axis pooling
was the intent throughout.
Measured cost: CONV sat pinned at ~40% balanced accuracy for 510 eras with
Sell recall 0%, while plain MLPs on the same data reached 57-61%. HYBRID,
which also carried this stage, came second-worst of the batch-norm group.
Not fixable in the topology: pooling one filter across time needs a stride
of window_out BETWEEN samples within a window, which a consecutive-window
kernel cannot express at any window/step. That needs a stride-aware kernel
in Network.cl + WarriorCPU.cpp + WarriorDML.cpp and a DLL rebuild, and is
only worth doing if a conv front-end earns its place without downsampling
first - with 20 sliding positions there is little to gain by halving them.
ConvPoolWindow/ConvPoolStep and their enums are removed with it, along with
the |CP: fingerprint term added earlier today.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:28:44 -04:00
|
|
|
//--- ConvPoolWindow / ConvPoolStep removed 2026-07-29. The pooling stage they configured reduced
|
|
|
|
|
//--- across FILTER channels rather than across time - a consequence of the conv layer's position-major
|
|
|
|
|
//--- output layout that no window/step pair can correct. See AddConvStage() in Expert\ExpertSignalAIBase.mqh.
|
refactor(ai): derive the dense taper's shape, not just its first layer
Deriving the first layer's width left NeuronsReduction and MinNeuronsCount
behind as inputs calibrated for something that no longer exists. Against a
hand-picked 500-wide first layer "keep 30%, floor at 20" produced a genuine
funnel - 500 -> 150 -> 45. Against the derived 64 it degenerates to
64 -> 20 -> 20: the reduction factor stops mattering after one step, and
"minimum neurons per layer" silently becomes the width of every layer but
the first. Two knobs whose labels no longer describe what they do.
The taper now runs geometrically from the derived first-layer width down to
a final hidden layer sized off the output count, spread evenly over however
many layers the chosen AIType implies:
MLP_3L 64 -> 28 -> 12 -> 3 29,151 dense weights
MLP_4L 64 -> 37 -> 21 -> 12 -> 3 30,450
CONV/LSTM/HYBRID_2L 64 -> 12 -> 3 27,763
and it stays a funnel at the floor, where the old rule could not:
D1 (first layer floored to 16) 16 -> 14 -> 12 -> 3
Both inputs are removed. With the width derived there is no freedom left in
the taper, so keeping either would only let the user contradict the
derivation. The layer COUNT stays selectable, because it is bundled into
AIType alongside the conv/LSTM front-end - depth is an architecture choice,
not a data-derived quantity, and pairing them means the two cannot
contradict each other.
m_minNeuronsCount / m_neuronsReduction survive as frozen members: nothing
reads them to build a topology any more, but they hold positional slots in
the .cfg sidecar and the weights fingerprint, and changing either value
would re-key every model on disk for no behavioural reason.
The DB config fingerprint drops both terms.
Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:03:42 -04:00
|
|
|
//--- No "min neurons" / "reduction per layer" inputs either. With the first layer's width derived
|
|
|
|
|
//--- (ComputeFirstLayerWidth) the taper has no freedom left: it runs geometrically from that width down
|
|
|
|
|
//--- to a final hidden layer sized off the output count, spread over the layer count the chosen AIType
|
|
|
|
|
//--- implies. Keeping either knob would let the user contradict the derivation - and both were
|
|
|
|
|
//--- calibrated for the old hand-picked 500-wide first layer, where they gave 500->150->45; against the
|
|
|
|
|
//--- derived 64 they degenerate to 64->20->20. See BuildFreshTopology()'s taper block.
|
feat(ai): batch normalization between dense layers
The only bounded stage in the entire forward path was the sigmoid
classification head - every hidden stage is PRELU. That is a network with
no internal scale control, and the failure ordered exactly by depth: on
SP500 H1 the shallow perceptron held ~52% balanced accuracy while the
deepest topology sat on the 33.3% one-class floor, with the per-bar logit
spread decaying monotonically (0.45 -> 0.38 over ~200 eras) until the
evidence tilt fell under the class-prior tilt. That is the signature of
internal covariate shift, which chapter 6.1 of the reference book is
entirely about and which the NeuroNet_DNG engine addresses with a layer
this project never had.
Two mechanisms make this the right fix rather than more hyperparameter
nudging:
- it decouples WEIGHT_DECAY from the learned function (van Laarhoven
2017) - with a normalized layer downstream, decay can no longer grind
the discriminative signal away, it only rescales the effective
learning rate;
- it is the precondition for ever running an unbounded logit head here.
The 2026-07-27 attempt blew up (IS error 5.6e15) precisely because
nothing upstream constrained scale.
Implementation notes:
- CNeuronBatchNormOCL computes host-side rather than as a fourth copy of
a kernel across Network.cl + WarriorCPU.cpp + WarriorDML.cpp. The math
is elementwise O(n); this way it behaves identically on all four
compute tiers, needs no DLL rebuild, and cannot drift between
backends. Same precedent as the softmax+CCE gradient and the
per-sample loss weighting, both computed in MQL5 for that reason.
- Statistics are exponential moving, not a stored mini-batch: training
is pure online SGD, one update per sample, so there is no batch to
average over. BatchNormWindow is an EMA window length.
- gamma/beta are excluded from weight decay, deliberately - decaying
gamma toward zero is the exact pathology being fixed.
- The layer self-sizes from whatever sits below it, because a conv/pool
stage's output width is derived inside the CNet constructor and is not
knowable to the topology builder.
- Checkpoint capture/restore/blend carry gamma/beta and the running
statistics alongside the dense matrix, so the plateau ladder cannot
restore a mismatched pair.
- SeedOutputLayerBias accepted only an exact defNeuronBaseOCL as the
weight-carrying penultimate layer; with normalization enabled that is
the batch-norm layer, so the cold-start bias seed would have silently
stopped being applied.
- Refuses to build, loudly, if a topology asks for normalization with no
compute backend at all - rather than quietly training a different
architecture than the one requested.
EnableBatchNorm (default on) and BatchNormWindow (1000 samples) are
inputs so the effect can be A/B'd without a recompile. Both feed the
weights-filename fingerprint, appended conditionally so existing non-BN
configs keep their fingerprints and are not forced to retrain.
Verified: analytic gradients match finite differences to 1.5e-7 relative
over 200 random cases; a faithful port of the full forward/backward chain
collapses to the 33.3% floor by era 4 without this layer and holds
36-43% with it. Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 12:34:29 -04:00
|
|
|
//--- Batch normalization (Ioffe & Szegedy 2015) between every pair of dense layers, including just
|
|
|
|
|
//--- before the classification head. ON by default: without it the only bounded stage in the whole
|
|
|
|
|
//--- forward path was the sigmoid head, and the observed failure mode ordered exactly by depth - the
|
|
|
|
|
//--- shallow perceptron held ~52% balanced accuracy while the deepest topology sat on the 33.3%
|
|
|
|
|
//--- one-class floor. It also decouples WEIGHT_DECAY from the learned function, which is what stops
|
|
|
|
|
//--- the slow monotonic decay of the per-bar logit spread that preceded every collapse.
|
|
|
|
|
//--- Left as an input rather than hardcoded so the effect can be A/B'd without a recompile. It is part
|
|
|
|
|
//--- of the weights-filename fingerprint, so flipping it starts a separate model rather than resuming
|
|
|
|
|
//--- an incompatible one. See AI\NeuronBatchNorm.mqh.
|
feat: make batch normalization mandatory, and record the run-3 results
EnableBatchNorm and BatchNormWindow demoted from inputs to constants. Batch
norm is required, not optional: measured on identical MLP_3L topologies it
was worth +11.3 points of balanced accuracy (57.0% with, 45.7% without),
stable across 150+ and 200+ eras, and the no-BN control converged to ~5% IS
and OOS accuracy with no chart signals at all. A user cannot make a good
decision here and can easily make a ruinous one, so the choice is not
offered. BatchNormWindow goes with it - a running-statistics window in
samples has no meaningful setting a trader could reason about, and its only
other reachable state (<=1) silently disables the layer.
Kept as named constants rather than deleted: the topology builder, the
weights fingerprint and the .cfg guard all read them, and a constant keeps
those paths - and the ability to flip one for a diagnostic rebuild - intact.
Fewer knobs also means a shorter Market description and less room for a
buyer to misconfigure.
EXPERIMENTS.md records runs 2 and 3, since the MT5 logs are wiped between
runs and these measurements are what the design decisions rest on. Run 3
(12h, uncapped tau=1.0) is a write-off: zero eras out of 1,993 across the
five batch-norm charts ever called a direction on fewer than half of all
bars, at a median precision equal to the ~6.1% base rate. The damage was
present at era 1 and never recovered over 292-766 eras.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 08:51:10 -04:00
|
|
|
//--- NOT an input. Batch normalization is required, not optional: measured 2026-07-29 on identical
|
|
|
|
|
//--- MLP_3L topologies it was worth +11.3 points of balanced accuracy (57.0% with, 45.7% without),
|
|
|
|
|
//--- stable across 150+ and 200+ eras, and the no-BN control converged to ~5% IS and OOS accuracy
|
|
|
|
|
//--- with no chart signals at all. A user cannot make a good decision here and can easily make a
|
|
|
|
|
//--- ruinous one, so the choice is not offered. Kept as a named constant rather than deleted: the
|
|
|
|
|
//--- topology builder, the weights fingerprint and the .cfg guard all read it, and a constant keeps
|
|
|
|
|
//--- those paths (and the ability to flip it for a diagnostic rebuild) intact.
|
|
|
|
|
const bool EnableBatchNorm = true; // AI: batch normalization
|
feat(ai): batch normalization between dense layers
The only bounded stage in the entire forward path was the sigmoid
classification head - every hidden stage is PRELU. That is a network with
no internal scale control, and the failure ordered exactly by depth: on
SP500 H1 the shallow perceptron held ~52% balanced accuracy while the
deepest topology sat on the 33.3% one-class floor, with the per-bar logit
spread decaying monotonically (0.45 -> 0.38 over ~200 eras) until the
evidence tilt fell under the class-prior tilt. That is the signature of
internal covariate shift, which chapter 6.1 of the reference book is
entirely about and which the NeuroNet_DNG engine addresses with a layer
this project never had.
Two mechanisms make this the right fix rather than more hyperparameter
nudging:
- it decouples WEIGHT_DECAY from the learned function (van Laarhoven
2017) - with a normalized layer downstream, decay can no longer grind
the discriminative signal away, it only rescales the effective
learning rate;
- it is the precondition for ever running an unbounded logit head here.
The 2026-07-27 attempt blew up (IS error 5.6e15) precisely because
nothing upstream constrained scale.
Implementation notes:
- CNeuronBatchNormOCL computes host-side rather than as a fourth copy of
a kernel across Network.cl + WarriorCPU.cpp + WarriorDML.cpp. The math
is elementwise O(n); this way it behaves identically on all four
compute tiers, needs no DLL rebuild, and cannot drift between
backends. Same precedent as the softmax+CCE gradient and the
per-sample loss weighting, both computed in MQL5 for that reason.
- Statistics are exponential moving, not a stored mini-batch: training
is pure online SGD, one update per sample, so there is no batch to
average over. BatchNormWindow is an EMA window length.
- gamma/beta are excluded from weight decay, deliberately - decaying
gamma toward zero is the exact pathology being fixed.
- The layer self-sizes from whatever sits below it, because a conv/pool
stage's output width is derived inside the CNet constructor and is not
knowable to the topology builder.
- Checkpoint capture/restore/blend carry gamma/beta and the running
statistics alongside the dense matrix, so the plateau ladder cannot
restore a mismatched pair.
- SeedOutputLayerBias accepted only an exact defNeuronBaseOCL as the
weight-carrying penultimate layer; with normalization enabled that is
the batch-norm layer, so the cold-start bias seed would have silently
stopped being applied.
- Refuses to build, loudly, if a topology asks for normalization with no
compute backend at all - rather than quietly training a different
architecture than the one requested.
EnableBatchNorm (default on) and BatchNormWindow (1000 samples) are
inputs so the effect can be A/B'd without a recompile. Both feed the
weights-filename fingerprint, appended conditionally so existing non-BN
configs keep their fingerprints and are not forced to retrain.
Verified: analytic gradients match finite differences to 1.5e-7 relative
over 200 random cases; a faithful port of the full forward/backward chain
collapses to the 33.3% floor by era 4 without this layer and holds
36-43% with it. Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 12:34:29 -04:00
|
|
|
//--- EMA window the running mean/variance are estimated over, in TRAINING SAMPLES (bars replayed),
|
|
|
|
|
//--- not eras. Training here is pure online SGD - one update per sample - so there is no mini-batch to
|
|
|
|
|
//--- average over and this stands in for the batch size. Long enough to be a stable estimate of the
|
|
|
|
|
//--- feature distribution, short enough to track a genuine regime change. 1000 is ~3% of a typical
|
|
|
|
|
//--- 36k-bar in-sample window.
|
feat: make batch normalization mandatory, and record the run-3 results
EnableBatchNorm and BatchNormWindow demoted from inputs to constants. Batch
norm is required, not optional: measured on identical MLP_3L topologies it
was worth +11.3 points of balanced accuracy (57.0% with, 45.7% without),
stable across 150+ and 200+ eras, and the no-BN control converged to ~5% IS
and OOS accuracy with no chart signals at all. A user cannot make a good
decision here and can easily make a ruinous one, so the choice is not
offered. BatchNormWindow goes with it - a running-statistics window in
samples has no meaningful setting a trader could reason about, and its only
other reachable state (<=1) silently disables the layer.
Kept as named constants rather than deleted: the topology builder, the
weights fingerprint and the .cfg guard all read them, and a constant keeps
those paths - and the ability to flip one for a diagnostic rebuild - intact.
Fewer knobs also means a shorter Market description and less room for a
buyer to misconfigure.
EXPERIMENTS.md records runs 2 and 3, since the MT5 logs are wiped between
runs and these measurements are what the design decisions rest on. Run 3
(12h, uncapped tau=1.0) is a write-off: zero eras out of 1,993 across the
five batch-norm charts ever called a direction on fewer than half of all
bars, at a median precision equal to the ~6.1% base rate. The damage was
present at era 1 and never recovered over 292-766 eras.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 08:51:10 -04:00
|
|
|
//--- Also not an input, for the same reason plus one more: this is a running-statistics window in
|
|
|
|
|
//--- SAMPLES, and nothing on the Inputs tab tells a trader what a good value is. It only ever had
|
|
|
|
|
//--- two meaningful settings - large enough to be a stable estimate, or <=1 which silently disables
|
|
|
|
|
//--- the layer entirely. The first is the only correct one.
|
|
|
|
|
const int BatchNormWindow = 1000; // AI: batch-norm window (samples)
|
feat(nn): derive dense depth, train on all history, pin the shape in .cfg
Completes the derived-topology work. Three inputs removed.
AIType loses its depth suffix - AI_MLP/AI_CONV/AI_LSTM/AI_HYBRID, five
entries instead of eight. Depth is now derived from the two endpoints
the taper already has to connect (derived first-layer width, output-tied
final width) at a 2x per-layer compression target, clamped [2..5].
Asking a user to pick a layer count while the code derives the widths
those layers taper between was asking for half a decision: at 64 units
tapering to 12, four layers compress by 1.4x per step and five by 1.3x,
so the extra depth bought no abstraction. On the shipping H1/10y default
the derivation lands on 3 layers - the depth that actually won Run 2.
StudyPeriods removed. There is no case for training on less data than
the broker provides at a ~6% directional base rate; the honest
generalization read comes from the OOS holdout, not from withholding
history. Training now starts at the earliest available bar, floored by
MinTrainYear, which answers a different question (excluding dubious
pre-history) and stays.
That required closing the hazard the old code documented: the capacity
budget now MEASURES the symbol's real bar count, and a topology derived
from a measurement would widen as history downloads. Both ends are now
pinned. Every derived value left the weights-filename fingerprint -
keying a filename on a measured quantity means the EA looks for a file
that does not exist, starts from era 0 and orphans a trained model,
silently, because a missing cache is the normal first-run state. The
shape lives in the .cfg instead, where LoadAndCompare now ADOPTS the
four derived fields rather than diffing them; a mismatch there would
discard a fully-trained model over nothing the user did. Two fields
appended to the .cfg for the conv/LSTM stages, length-guarded on read
because FileReadInteger past EOF returns 0 with no error.
ForceHiddenLayers, a compile-time constant like DebuggingMode, pins
depth for diagnostic comparisons. It joins the fingerprint only when
non-zero, so forced depths get their own files - sequential comparisons
only, not simultaneous from one .ex5.
Derived shape, H1/10y defaults (21 features x 20 bars): first layer 64,
3 dense, 8 conv filters, 16 LSTM units. The LSTM block halves from
~58k to ~28k weights.
Both builds compile 0 errors, 0 warnings. Re-keys existing models.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 10:05:40 -04:00
|
|
|
//--- No "training years" input either, removed 2026-07-30. There is no case for training on less data
|
|
|
|
|
//--- than the broker actually provides: this is a weak signal at a ~6% directional base rate, every extra
|
|
|
|
|
//--- year is more of the minority class, and the honest generalization read comes from the out-of-sample
|
|
|
|
|
//--- holdout below rather than from withholding history. Training now starts at the earliest available
|
|
|
|
|
//--- bar (floored by MinTrainYear, which exists to exclude a broker's dubious pre-history, not to size
|
|
|
|
|
//--- the run). The topology's capacity budget reads the REAL bar count that yields - see
|
|
|
|
|
//--- CExpertSignalAIBase::EstimatedInSampleBars(), and the note there on why that measurement is taken
|
|
|
|
|
//--- exactly once and then pinned.
|
2026-07-22 13:33:56 -04:00
|
|
|
input OOS_SPLIT_PRESET OOSSplit = OOS_30; // Out-of-sample holdout
|
2026-07-25 15:55:56 -04:00
|
|
|
//--- There is deliberately NO "target accuracy" input. Training runs until it stops improving and then
|
|
|
|
|
//--- deploys its own best model: after a stretch of eras with no new best it tries to escape the plateau
|
|
|
|
|
//--- (learning-rate warm restart, then focal-gamma anneal), and if neither finds anything better it
|
|
|
|
|
//--- finalises the best checkpoint it found. See the PLATEAU_* ladder in Expert\ExpertSignalAIBase.mqh.
|
|
|
|
|
//--- An absolute target could only ever be wrong in one of two directions: set above what a given
|
|
|
|
|
//--- symbol/timeframe can reach and the run never converges (it burns to the era cap and deploys the same
|
|
|
|
|
//--- checkpoint hours later anyway); set below and it stops a run that was still getting better.
|
|
|
|
|
//--- MinRecall stays, and is NOT a performance target - it is the anti-collapse floor that makes
|
|
|
|
|
//--- auto-deploy safe. Buy, Sell AND Neutral must each be recognised this well on held-back data before a
|
|
|
|
|
//--- checkpoint is eligible to ship, so a model that quietly gives up on one direction can never deploy.
|
|
|
|
|
//--- It is an OOS CLASSIFICATION metric (3-class), NOT a trade win rate: random guessing is ~33%.
|
fix(training): escape the recall-gate catch-22 that let runs decay unchecked
Evidence (MQL5\Logs, SP500 H1, 2026-07-29):
Perceptron era 61 Buy 32% Sell 27% Neut 94% bal 51%
LSTM era 160 Buy 16% Sell 11% Neut 98% bal 42% (peaked 49% @ era 44)
Hybrid era 179 Buy 5% Sell 2% Neut 99% bal 35% (peaked 41%)
CONV era 228 Buy 2% Sell 4% Neut 99% bal 35% (peaked 40% @ era 122)
Every model peaks early then decays monotonically toward Neutral, and nothing
stops it: the restore-best-weights + decay-eta handler is gated on
m_bestPassedRecall, which stays false forever when no checkpoint ever clears the
per-class floor. CONV ran 228 eras with eta pinned at its 0.000300 start. The
plateau ladder cannot end such a run either (stage 3 refuses to deploy without a
recall pass, so it resets ~27 times), making it a 1000-era one-way trip.
The gate's own justification had expired. It was written when the pre-pass
tiebreak was blended-accuracy-only, where "best" really did mean "called Neutral
most confidently". The balanced-selection change replaced that with
`balancedOosEra > m_bestBalancedOos` plus an isFullyCollapsedEra exclusion, so a
Neutral-only era now scores ~33% - the FLOOR of the balanced metric - and cannot
anchor the checkpoint at all. Pre-pass "best" now means "most class-balanced so
far", which is worth defending; and isWorseEra is itself a balanced-accuracy
regression, so it cannot fire merely for trading Neutral calls for Buy/Sell.
The original concern still holds while the best-so-far IS near-collapse, so the
escape is margin-guarded: defend the checkpoint only once balanced accuracy sits
more than BALANCED_WORTH_DEFENDING_MARGIN_PCT (5pp) above the one-class floor of
100/3. Against the run above that engages for all three stuck topologies
(42.3/41.3/50.0 vs a 38.3 threshold) while a genuinely collapsed run still
explores freely.
Two inputs restored to the regime that actually produced a deploy:
- MinRecall 60 -> 40. The one successful auto-deploy in the logs (Hybrid, 28th
00:50, best balanced 66.0%) ran against a 40% floor. 60 has never been shown
reachable here - a floor above what the config can reach is the same "target
set too high" failure the surrounding comment already warns about.
- OversampleParity 60 -> 90. 60 overcorrected. Runs now START Neutral-dominant
(Buy 0-11% recall at era 1) and call Buy/Sell on 0-4% of bars against a ~6%
true base rate - under-calling, with no headroom to converge down from. The
deploying run began at Buy 90% / Sell 36%, 24% of bars called, and settled into
the floor from above. Raw over-calling is the intended starting condition; live
calls are base-rate-calibrated by AILogitPriorStrength, which is why the input's
own note says to judge over-calling by live-fired precision, not raw counts.
Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 07:51:08 -04:00
|
|
|
//--- 2026-07-29: 60 -> 40. 60 was never demonstrated reachable on this data. The ONE successful
|
|
|
|
|
//--- auto-deploy in the logs (Hybrid, SP500 H1, 28th 00:50, best balanced 66.0%) ran against a 40%
|
|
|
|
|
//--- floor; every run since has been gated at 60 and none has come close - CONV/LSTM/Hybrid peaked at
|
|
|
|
|
//--- 40/49/41% balanced and then decayed, so stage 3 refused to deploy and reset the ladder ~27 times,
|
|
|
|
|
//--- turning a converged run into a 1000-era one-way trip. A floor above what the configuration can
|
|
|
|
|
//--- reach is exactly the "absolute target set too high" failure the comment above warns about, just
|
|
|
|
|
//--- expressed per-class. Raise it again only after a run actually clears it with headroom.
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- 2026-08-01: NO LONGER AN INPUT. This is a safety floor, not a preference, and the one direction a
|
|
|
|
|
//--- user can move it is the harmful one - raising it past what the configuration reaches does not
|
|
|
|
|
//--- produce a better model, it produces NO model (nothing clears the gate, stage 3 refuses to deploy,
|
|
|
|
|
//--- and the run burns to the era cap). That failure was observed repeatedly at 60 and is the exact
|
|
|
|
|
//--- catch-22 this floor was nearly deleted over. 40 is the value the only successful auto-deploy in the
|
|
|
|
|
//--- project's history ran against. It also no longer decides what SHIPS - deployability moved to
|
|
|
|
|
//--- directional precision with a derived coverage floor - so it now only drives the diagnostic recall
|
|
|
|
|
//--- line, which makes exposing it even harder to justify.
|
|
|
|
|
const PERCENTAGE_PRESETS MinRecall = PCT_40;
|
2026-07-26 17:27:51 -04:00
|
|
|
//--- There are deliberately NO AI-only confidence inputs here any more. The AI entry floor and the AI
|
|
|
|
|
//--- early-exit threshold are the SAME two numbers the classic votes use - Min vote to open / Min
|
|
|
|
|
//--- opposite vote to close (Trade Management section) - so one pair of inputs governs both engines;
|
|
|
|
|
//--- see their declaration comment for how the 0-100 scale maps onto AI softmax confidence and tiers.
|
refactor(ai): nine class-imbalance inputs down to two
The imbalance section offered nine controls for one job. Audited against the
code, five of them did not do what their names said at the shipped defaults:
AILogitPriorStrength DEAD - Inference.mqh's post-hoc prior early-returns
whenever the adjusted loss is on, which is default.
OversampleParity DEAD in training - Training.mqh gated the replay loop
on !useLogitAdjustedLoss (correctly, citing Buda et
al. 2018). Live only in the online-learning path.
EnableMinorityReplay DEAD as replay. It survived ONLY as a focal-gamma
damper - "replay minority bars through pass-2
oversampling" was a focal-loss switch.
ConstrainReplay DEAD as a cap; it only chose damper 0.125 vs 0.25.
UseStaticPrior An exact duplicate of FreezePriorCalibration - the two
were OR'd together in the single place either is read.
So they were not five mechanisms fighting; they were one mechanism plus eight
knobs that mostly described machinery that no longer ran. That is worse than
a real conflict, because the log agreed with the names: the label-cache line
printed "reps up to 28x (90% parity) (seeding era 0's class-balance
oversampling)" on every run, describing an oversampling pass that had been
switched off. It is fixed here too - it cost this session a wrong diagnosis.
The one genuine redundancy was focal loss, running at gamma*0.125 alongside
the adjusted loss: two corrections on the same axis, the exact stacking
failure this file already cited Buda et al. for in two other places, damped
by a replay flag whose replay path was itself dead. Removed rather than
re-tuned. The plateau ladder is unaffected - its escape is the learning-rate
warm restart; the gamma anneal beside it only ever stepped toward zero.
WHAT REMAINS is logit-adjusted loss (Menon et al. 2021) plus a prior freeze:
LogitAdjustTau 0 = off; replaces the separate EnableLogitAdjusted-
Loss boolean, since a strength dial where 0 already
means off does not need an on/off switch beside it.
FreezePriorCalibration unchanged.
It is the only one of the six corrections with a consistency guarantee, and
it is consistent for exactly the balanced-error metric checkpoint selection
already ranks on - so the loss and the deploy decision optimize one thing.
The online continual-learning path keeps its own alpha-balanced focal weight,
now as constants pinned to the removed inputs' shipped defaults, so its
behaviour is unchanged. It legitimately needs its own correction:
ApplyLogitAdjustment() only runs inside a training run, so a deployed model
that was reloaded carries no logit offsets and would otherwise stream 31:1
data into itself uncorrected.
The weights-filename fingerprint is BYTE-IDENTICAL. The focal slot was a
double fed to a %d conversion and had always emitted a literal 0; the |MR:
segment is written as the constant its shipped defaults produced. Dropping
either would have re-keyed every model and forced a from-scratch retrain of
the one topology currently converged and trading.
Also removed as orphans: FOCAL_GAMMA_PRESET, MAX_OVERSAMPLE_REPLICAS,
OVERSAMPLE_PARITY_FRACTION, PLATEAU_GAMMA_STEP, and the now-unreachable
"neutralized by prior correction" diagnostic.
Both builds compile 0 errors, 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:46:57 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// CLASS IMBALANCE - ONE MECHANISM, ONE KNOB
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
//--- The directional base rate here is ~3% Buy / ~3% Sell / ~94% Neutral (a ~31:1 imbalance), so the
|
|
|
|
|
//--- loss needs SOME correction or the optimum is "always predict Neutral". This section used to offer
|
|
|
|
|
//--- NINE inputs for that one job. They were consolidated on 2026-07-31 because, audited against the
|
|
|
|
|
//--- code, five of them did not do what their names said at the shipped defaults:
|
|
|
|
|
//--- AILogitPriorStrength DEAD - Inference.mqh's post-hoc prior early-returns when the adjusted
|
|
|
|
|
//--- loss is on, because the offsets are already trained into the weights.
|
|
|
|
|
//--- OversampleParity DEAD in training - Training.mqh gates the replay loop on
|
|
|
|
|
//--- !useLogitAdjustedLoss (correctly: Buda et al. 2018 on why stacking
|
|
|
|
|
//--- oversampling with an analytic correction double-counts the imbalance).
|
|
|
|
|
//--- EnableMinorityReplay DEAD as replay; it survived ONLY as a focal-gamma damper (x0.125).
|
|
|
|
|
//--- ConstrainReplay DEAD as a cap; it only chose between damper 0.125 and 0.25.
|
|
|
|
|
//--- UseStaticPrior an exact duplicate of FreezePriorCalibration - the two were OR'd
|
|
|
|
|
//--- together in the single place either was read.
|
|
|
|
|
//--- Focal loss was the one real redundancy: it ran at gamma*0.125 alongside the adjusted loss, i.e.
|
|
|
|
|
//--- two corrections on the SAME axis, which is what the codebase's own Buda et al. citation warns
|
|
|
|
|
//--- against. Removed rather than re-tuned - the plateau ladder's escape is its learning-rate warm
|
|
|
|
|
//--- restart, and the gamma anneal it also performed was only ever a monotone step toward zero.
|
|
|
|
|
//---
|
|
|
|
|
//--- WHAT REMAINS is LOGIT-ADJUSTED LOSS (Menon et al. 2021, ICLR, "Long-tail learning via logit
|
|
|
|
|
//--- adjustment"): add tau*log(prior_c) to each class logit inside the TRAINING gradient only. The
|
|
|
|
|
//--- network learns to absorb the offset, so at inference its RAW argmax is already the
|
|
|
|
|
//--- balanced-error-optimal decision - no second correction at read time, by construction. Minimizing
|
|
|
|
|
//--- softmax cross-entropy on adjusted logits is consistent for BALANCED error, which is the metric
|
|
|
|
|
//--- checkpoint selection already ranks on, so the loss and the deploy decision optimize the same
|
|
|
|
|
//--- thing. It is the only one of the six with a consistency guarantee, which is why it is the one kept.
|
|
|
|
|
//---
|
|
|
|
|
//--- tau: 100% = tau 1.0, the paper's default and the only value carrying the guarantee. 0 = OFF, which
|
|
|
|
|
//--- is now the honest way to disable the correction entirely (it replaces the old EnableLogitAdjusted-
|
|
|
|
|
//--- Loss boolean - a separate on/off switch beside a strength dial where 0 already means off is two
|
|
|
|
|
//--- controls for one decision). NOTE the runtime auto-caps tau so the offsets cannot swamp the output
|
|
|
|
|
//--- head's usable logit range; the startup line reports the capped value actually used.
|
|
|
|
|
input LOGIT_PRIOR_STRENGTH_PRESETS LogitAdjustTau = LOGIT_PRIOR_100; // AI: class-imbalance correction (tau, 0=off)
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- Stop EMA-updating the measured class priors after the first real measurement. NOT AN INPUT as of
|
|
|
|
|
//--- 2026-08-01: it answers a question a user has no way to evaluate ("should the correction track this
|
|
|
|
|
//--- era's tally or the first one's"), and after the triple-barrier relabel the priors barely move
|
|
|
|
|
//--- between eras anyway - the labels are near-balanced and stable, which is the whole point of the
|
|
|
|
|
//--- relabel. Letting them track is the correct default; freezing exists for a symbol whose class
|
|
|
|
|
//--- distribution genuinely shifts mid-run, which is a developer's diagnostic, not a product setting.
|
|
|
|
|
const bool FreezePriorCalibration = false;
|
|
|
|
|
//--- SWING CONFIRM BARS IS NO LONGER AN INPUT (2026-08-01), but the constant is still load-bearing and
|
|
|
|
|
//--- must not be deleted. It stopped gating the LABELS with the triple-barrier relabel - that lookahead
|
|
|
|
|
//--- is now the barrier horizon, which is measured (ComputeBarrierHorizonBars) rather than configured.
|
|
|
|
|
//--- It still gates the swing-context INPUT FEATURES (EnableSwingContext, on by default, 9 features):
|
|
|
|
|
//--- ZigZag revises its most recent legs, so a feature that read the raw current buffer would be reading
|
|
|
|
|
//--- a value the live bar could not actually have had yet. That is straight lookahead into the feature
|
|
|
|
|
//--- vector, so this embargo stays - it simply has no reason to be user-facing, because the correct
|
|
|
|
|
//--- value is a property of the ZigZag indicator's own recalculation depth, not of anyone's preference.
|
|
|
|
|
//--- Kept in the weights fingerprint at its shipped value, so pinning it re-keys nothing.
|
|
|
|
|
const SWING_CONFIRMATION_PRESET SwingConfirmationBars = SC_100;
|
|
|
|
|
//--- Continual learning: after the model is deployed, keep adapting it on a LIVE chart to newly-RESOLVED
|
|
|
|
|
//--- market structure - the same supervised triple-barrier task it was trained on, waiting the full
|
|
|
|
|
//--- barrier horizon so a bar whose outcome is not yet decided is never learned from. The deployed model
|
|
|
|
|
//--- only moves toward the update while a rolling-accuracy guardrail holds; if accuracy decays the blend
|
|
|
|
|
//--- FREEZES (live keeps trading the last-good shadow while the net recovers), so drift cannot reach the
|
|
|
|
|
//--- account. No effect in the Strategy Tester/optimizer - the model is held fixed there by design.
|
|
|
|
|
//--- ON BY DEFAULT AND NO LONGER AN INPUT (2026-08-01). Adapting to a changing market is not an optional
|
|
|
|
|
//--- extra for a model that will be attached for months, it is the thing that keeps it from going stale,
|
|
|
|
|
//--- and the guardrail above is what makes it safe to leave on. See the caveat in the release checklist:
|
|
|
|
|
//--- this had never been forward-tested on a live feed at the time it was made default.
|
|
|
|
|
const bool EnableOnlineLearning = true;
|
|
|
|
|
//--- Default 6 -> 3 and NO LONGER AN INPUT (2026-08-01). Declustering existed because exact-pivot ZigZag
|
|
|
|
|
//--- labels make a same-direction repeat provably redundant; barrier labels answer every bar
|
|
|
|
|
//--- independently, so consecutive Buy setups inside a trend are real trades and suppressing them throws
|
|
|
|
|
//--- signal away - which argued for 0. It is not 0 because on D1 and above a 6-bar window spans more than
|
|
|
|
|
//--- a trading week, and two arrows a day apart on a weekly-scale move really are one event. 3 keeps the
|
|
|
|
|
//--- immediate-neighbour duplicate off the chart on slow timeframes while leaving genuine consecutive
|
|
|
|
|
//--- setups intact on fast ones. Display/emission only either way - the raw per-bar recall/precision
|
|
|
|
|
//--- metrics are never declustered, so this cannot flatter a model's measured numbers.
|
2026-08-10 14:26:12 -04:00
|
|
|
//--- 10 bars (2026-08-10, was 3). On H1 a 3-bar window collapsed only the tightest runs and left
|
|
|
|
|
//--- visible clusters around every turn; 10 bars is a third of a session and closer to the spacing of
|
|
|
|
|
//--- genuinely distinct setups on this timeframe. Applies to every topology - it is not per-network.
|
|
|
|
|
const int SignalClusterWindow = 10;
|
feat: excursion-size head (Stage 1, measurement only)
Direction is closed - normalised asymmetry fails on three instruments
with a working positive control, and the classifier's own best-of-999
era-cap test agrees (+0.9pp = 1.48 sigma, family-wise p=1.0000). SIZE is
a different question and RANGE clears at ~4x its null.
Checked the denomination before building on that, since the source memo
warns to: m_excUpCache holds (maxHigh - fill)/ATR, so "RANGE is
predictable" is a claim about travel RELATIVE to current ATR, not a
restatement of "ATR is autocorrelated". It is exactly the part a fixed
multiple (stop 3.31*ATR, target 1.64*ATR) discards.
A second small CNet, 760 -> 24 -> 32 sigmoid outputs = P(price reaches
ladder rung k) upward and downward. Survival parameterisation rather than
regressing the multiple, because it needs nothing new from CNet: sigmoid
outputs and the per-neuron delta the `total != 3` branch already applies
(a quantile head would need a linear activation and a pinball gradient in
Network.mqh, Network.cl and the DirectML path, on a class four topologies
share). Targets are free - m_ladderUpAt already records first-touch age
per rung with 0 meaning never reached.
Separate net, not extra outputs on the classifier: more outputs would
change m_outputNeuronsCount, the .nnw shape and the fingerprint, and push
the count off 3 - the exact condition backProp uses to select the joint
softmax gradient the 3-class head depends on. The classifier is
bit-for-bit unaffected and this is removable without trace.
STAGE 1 PLACES NO ORDERS. It reports a Brier skill score against the
constant per-rung base rate - the baseline a fixed ATR multiple already
assumes - with both predictors fitted IS and evaluated OOS, so neither
gets a look at the test set. Positive skill justifies Stage 2 (drive
SL/TP and sizing off ExcursionQuantile, which is defined and deliberately
uncalled). Zero or negative means ATR already carries everything and
Stage 2 must not be built.
Trains only on primary occurrences: the replay queue oversamples for
CLASS balance, and a direction-balanced sample is a biased SIZE sample.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 07:40:01 -04:00
|
|
|
//--- EXCURSION-SIZE HEAD (Expert\AIBase\Excursion.mqh). A second small net that predicts how FAR price
|
|
|
|
|
//--- travels within the horizon - never which way, which is measured-closed on three instruments.
|
|
|
|
|
//--- STAGE 1 IS A MEASUREMENT: it trains beside the classifier and prints a Brier skill score against
|
|
|
|
|
//--- the constant base rate a fixed ATR multiple already assumes. It places no orders and moves no
|
|
|
|
|
//--- stops, so leaving it on costs only era time and leaving it off changes nothing else.
|
|
|
|
|
//--- NOT in the weights fingerprint: it is a separate network with its own weights, so it cannot alter
|
|
|
|
|
//--- the classifier's shape - the rule that keeps TRAIN_BATCH_SIZE out for the same reason.
|
|
|
|
|
const bool UseExcursionHead = true;
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- Era cap. NOT AN INPUT as of 2026-08-01: it is a runaway backstop, not a training control. Training
|
|
|
|
|
//--- decides its own ending (the plateau ladder deploys the best checkpoint once escalation stops finding
|
|
|
|
|
//--- anything better), so in a healthy run this number is never reached and choosing it changes nothing;
|
|
|
|
|
//--- in an unhealthy one the useful response is to read the era log, not to raise a cap.
|
|
|
|
|
const MAX_ERAS_PRESET MaxErasPerRun = ME_1000;
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
// AI INPUT FEATURES (the data the neural network sees each bar)
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string AISignals = "AI Input Features"; // AI Input Features
|
2026-08-11 21:53:37 -04:00
|
|
|
//--- ind_Periods IS NO LONGER AN INPUT (2026-08-11). The number of bars per input sequence is now
|
|
|
|
|
//--- DERIVED - median confirmed swing leg over recent history, snapped to a coarse ladder and capped
|
|
|
|
|
//--- (see DeriveHistoryBars) - then pinned in the model's .cfg and ADOPTED on every later load, the
|
|
|
|
|
//--- same measure-once contract as the barrier geometry. Picking it by predictive skill instead was
|
|
|
|
|
//--- ruled out by the 2026-08-06 lag profile (no information at any lag 0-20): a best-of-N window
|
|
|
|
|
//--- scan would only ever mine noise. The ATR feature/barrier-unit lookback it also used to set is
|
|
|
|
|
//--- deliberately DECOUPLED and pinned at the old default below: the ATR indicator is created before
|
|
|
|
|
//--- the .cfg can be adopted, so deriving its period would let init ordering change the unit the
|
|
|
|
|
//--- pinned SL/TP multiples are expressed in.
|
|
|
|
|
#define ATR_FEATURE_PERIOD 20
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
input ENUM_APPLIED_VOLUME VolumeData = VOLUME_TICK; // Volume data type (tick / real)
|
|
|
|
|
input bool EnableVolume = true; // Feature: volume
|
|
|
|
|
input bool EnableTime = true; // Feature: time
|
|
|
|
|
input bool EnableATR = true; // Feature: volatility (ATR)
|
|
|
|
|
//--- MA/RSI as network input features, independent of the Classic Signals votes above (you can feed MA
|
|
|
|
|
//--- to the model without it voting, or vice versa). Uses PeriodMA/MA_Type/PeriodRSI (Classic Signals)
|
|
|
|
|
//--- as the starting period, then auto-tuned from there when Auto-tune indicators is on.
|
|
|
|
|
input bool EnableMAFeature = true; // Feature: Moving Average
|
|
|
|
|
input bool EnableRSIFeature = false; // Feature: RSI
|
|
|
|
|
//--- MACD adds 3 inputs/bar (main, signal, histogram - all ATR-normalized); Ichimoku adds 8 (distances to
|
|
|
|
|
//--- Tenkan/Kijun/both cloud edges, the TK spread, cloud thickness here and projected, and the Chikou
|
|
|
|
|
//--- displacement). Widths are per BAR, so each is multiplied by Bars to analyse before it reaches the
|
|
|
|
|
//--- first layer - Ichimoku at the default 20 bars is 160 extra inputs on its own. Worth it for the
|
|
|
|
|
//--- multi-timescale structure nothing else in the vector carries, but enable deliberately, not by habit.
|
|
|
|
|
input bool EnableMACDFeature = false; // Feature: MACD
|
|
|
|
|
input bool EnableIchimokuFeature = false; // Feature: Ichimoku
|
|
|
|
|
//--- Confirmed ZigZag swing direction/magnitude/age - lookahead-safe (repainting embargo applied, see
|
|
|
|
|
//--- SWING_CONFIRMATION_BARS in Expert\ExpertSignalAIBase.mqh).
|
|
|
|
|
input bool EnableSwingContext = true; // Feature: ZigZag swing context
|
|
|
|
|
input bool EnableADCumulativeDelta = false; // Feature: Cumulative Delta
|
2026-08-02 01:09:18 -04:00
|
|
|
input bool EnableADShorteningOfThrust = true; // Feature: Shortening of Thrust
|
|
|
|
|
input bool EnableADWyckoffEventStream = true; // Feature: Wyckoff Events
|
|
|
|
|
input bool EnableADWyckoffFailedStructure = true; // Feature: Wyckoff Failed Structure
|
|
|
|
|
input bool EnableADWyckoffSignificantBarInversion = true; // Feature: Wyckoff Bar Inversion
|
feat: expose the AD/Wyckoff parameters; default the indicator tuner off
AutoTuneIndicators now defaults to FALSE, and the 33 AD/Wyckoff parameters
it used to search are now inputs.
WHY THE DEFAULT FLIPPED - not because the search is broken. It is correct,
and its own Sidak gate is what proves it: 324 candidates per model on
SP500 H1, "no improvement" on all four topologies (0.00236 -> 0.00236 on
the AD configs, 0.00370 -> 0.00370 on PAI), winner rejected at p=1.0000.
It cannot do better here by construction - it ranks candidates by MARGINAL
MI, and the headline MI is 0.00370 nats against a shuffled null of
0.00379 +/- 0.00061 (p=0.4975), so every candidate is a noise draw and the
maximum over N of them is noise too. The cost is 45-56 min per model in
one synchronous call with no yield, and it was the amplifier for the
handle leak fixed in 33f106d. The EA's own report says it plainest: "no
per-feature indicator retuning will help."
THE INPUT STAYS. TuneIndicatorsByFilter is one function of twelve in
AIBase/AutoTune.mqh; the other eleven are the MI/lag/excursion/geometry
diagnostics that produced every verdict this project relies on, and they
run regardless of this flag. Removing the input invites removing the file.
WHY THE INPUTS WERE NEEDED. All 33 were literals in CADIndicatorTuner's
constructor with no input of any kind, while MA/RSI/MACD/Ichimoku have had
their periods exposed from the start. On the AD configs those indicators
contribute 28 of 64 features per bar. Survivable while the tuner searched
them; indefensible with it off, where they would freeze at values nobody
chose.
CONSOLIDATED 33 -> 18. volClimax/volHigh/rangeClimax/rangeSignificant/
stVolRatio/atr were duplicated verbatim across CumulativeDelta, Wyckoff
Events, Failed Structure and Bar Inversion - the same constants restated
3-4 times. One concept, one input. They are SEEDS: each fans out to the
indicator's own struct field, so with the tuner on it retains full
per-indicator freedom to move them apart. Same contract as PeriodMA.
NO RETRAIN. Every default is byte-identical to the literal it replaces,
and the fingerprint's new ADP token is appended ONLY on deviation
(MACD/Ichimoku/BN/XA convention), gated on the AD features being enabled.
At defaults the token is absent, so every model on disk keeps its filename
and stays loadable. Without that guard, merely EXPOSING these parameters
would have re-keyed every config and forced a from-scratch retrain of all
four topologies for a change that alters no number anywhere.
All-or-nothing rather than per-input, so the token can never encode a
partial picture of what the features were built from.
Compiles clean: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 20:37:21 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// AD / WYCKOFF INDICATOR PARAMETERS
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
//--- ADDED 2026-08-08, because AutoTuneIndicators now defaults to false and these 33 values had NO input
|
|
|
|
|
//--- of any kind - they were literals in CADIndicatorTuner's constructor. MA/RSI/MACD/Ichimoku have had
|
|
|
|
|
//--- their periods exposed since the beginning; the order-flow and Wyckoff indicators, which contribute
|
|
|
|
|
//--- 28 of the 64 features per bar on the AD configs, were operator-invisible. With the tuner on that was
|
|
|
|
|
//--- survivable (it searched them); with it off they would be frozen at values nobody chose.
|
|
|
|
|
//---
|
|
|
|
|
//--- SEEDS, exactly like PeriodMA/MA_Type. When AutoTuneIndicators is on, the search still starts here and
|
|
|
|
|
//--- is still free to move each indicator's copy independently - collapsing the shared thresholds below
|
|
|
|
|
//--- into one input each constrains only what the OPERATOR sets, never what the tuner may explore.
|
|
|
|
|
//---
|
|
|
|
|
//--- CONSOLIDATED 33 -> 18 on purpose, following the imbalance-input precedent. volClimax/volHigh/
|
|
|
|
|
//--- rangeClimax/rangeSignificant/stVolRatio/atr were duplicated verbatim across CumulativeDelta, Wyckoff
|
|
|
|
|
//--- Events, Failed Structure and Bar Inversion - the same four constants restated 3-4 times each. They
|
|
|
|
|
//--- are one CONCEPT per row ("what counts as climactic volume", "what counts as a significant range"),
|
|
|
|
|
//--- so they get one input per concept. Eighteen knobs an operator can reason about beats thirty-three
|
|
|
|
|
//--- that invite inconsistent settings for the same idea.
|
|
|
|
|
//---
|
|
|
|
|
//--- Every default below is byte-identical to the literal it replaces, so this ships as a pure no-op:
|
|
|
|
|
//--- see the ADP token in ConfigFingerprint(), which is appended ONLY when something actually differs,
|
|
|
|
|
//--- leaving every existing model's filename - and therefore its trained weights - untouched.
|
|
|
|
|
#define WYK_VOL_CLIMAX_DEF 2.5
|
|
|
|
|
#define WYK_VOL_HIGH_DEF 1.5
|
|
|
|
|
#define WYK_RANGE_CLIMAX_DEF 1.8
|
|
|
|
|
#define WYK_RANGE_SIGNIF_DEF 1.2
|
|
|
|
|
#define WYK_ST_VOL_RATIO_DEF 0.6
|
|
|
|
|
#define WYK_ATR_MULT_DEF 0.5
|
|
|
|
|
#define ADCD_LOOKBACK_DEF 50
|
|
|
|
|
#define SOT_THRUST_LOOKBACK_DEF 30
|
|
|
|
|
#define SOT_MIN_IMPULSES_DEF 3
|
|
|
|
|
#define SOT_THRESHOLD_DEF 0.30
|
|
|
|
|
#define WES_LOOKBACK_DEF 50
|
|
|
|
|
#define WES_ZIGZAG_DEF 3
|
|
|
|
|
#define WES_TOUCH_ATR_DEF 0.5
|
|
|
|
|
#define WES_AR_MIN_ATR_DEF 1.0
|
|
|
|
|
#define WES_MAX_RANGE_BARS_DEF 200
|
|
|
|
|
#define WFS_LOOKBACK_DEF 50
|
|
|
|
|
#define WFS_ZIGZAG_STRENGTH_DEF 3
|
|
|
|
|
#define WSBI_LOOKBACK_DEF 50
|
|
|
|
|
input string ADParams = "AD / Wyckoff Parameters"; // AD / Wyckoff Parameters
|
|
|
|
|
//--- Shared thresholds. Each is a multiple of that bar's own rolling average (volume) or ATR (range), so
|
|
|
|
|
//--- they are scale-free and mean the same thing on any symbol.
|
|
|
|
|
input double Wyk_VolClimaxMult = WYK_VOL_CLIMAX_DEF; // Climactic volume (x average)
|
|
|
|
|
input double Wyk_VolHighMult = WYK_VOL_HIGH_DEF; // High volume (x average)
|
|
|
|
|
input double Wyk_RangeClimaxMult = WYK_RANGE_CLIMAX_DEF; // Climactic range (x ATR)
|
|
|
|
|
input double Wyk_RangeSignificantMult = WYK_RANGE_SIGNIF_DEF; // Significant range (x ATR)
|
|
|
|
|
input double Wyk_ShortTermVolRatio = WYK_ST_VOL_RATIO_DEF; // Short-term volume ratio
|
|
|
|
|
input double Wyk_AtrMult = WYK_ATR_MULT_DEF; // General ATR multiple
|
|
|
|
|
//--- Per-indicator structure. Lookbacks are in BARS and set how far back each indicator searches for the
|
|
|
|
|
//--- structure it names; they do not need to agree with each other or with HistoryBars.
|
|
|
|
|
input int ADCD_Lookback = ADCD_LOOKBACK_DEF; // Cumulative Delta: lookback bars
|
|
|
|
|
input int SOT_ThrustLookback = SOT_THRUST_LOOKBACK_DEF; // Shortening of Thrust: lookback bars
|
|
|
|
|
input int SOT_MinImpulses = SOT_MIN_IMPULSES_DEF; // Shortening of Thrust: min impulses
|
|
|
|
|
input double SOT_Threshold = SOT_THRESHOLD_DEF; // Shortening of Thrust: threshold
|
|
|
|
|
input int WES_Lookback = WES_LOOKBACK_DEF; // Wyckoff Events: lookback bars
|
|
|
|
|
input int WES_ZigZag = WES_ZIGZAG_DEF; // Wyckoff Events: ZigZag strength
|
|
|
|
|
input double WES_TouchATR = WES_TOUCH_ATR_DEF; // Wyckoff Events: zone touch (x ATR)
|
|
|
|
|
input double WES_ARMinATR = WES_AR_MIN_ATR_DEF; // Wyckoff Events: min AR size (x ATR)
|
|
|
|
|
input int WES_MaxRangeBars = WES_MAX_RANGE_BARS_DEF; // Wyckoff Events: max range life (bars)
|
|
|
|
|
input int WFS_Lookback = WFS_LOOKBACK_DEF; // Failed Structure: lookback bars
|
|
|
|
|
input int WFS_ZigZagStrength = WFS_ZIGZAG_STRENGTH_DEF; // Failed Structure: ZigZag strength
|
|
|
|
|
input int WSBI_Lookback = WSBI_LOOKBACK_DEF; // Bar Inversion: lookback bars
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- News LAST in this list on purpose: it is the only feature whose data comes from outside the price
|
|
|
|
|
//--- series (the terminal's economic calendar), so it is the one a user is most likely to want to reason
|
|
|
|
|
//--- about separately - and the only one with a companion setting. Proximity/impact only, never
|
|
|
|
|
//--- actual-vs-forecast, which is not knowable ahead of the release.
|
|
|
|
|
input bool EnableNews = false; // Feature: news proximity
|
|
|
|
|
input NF_LOOKBACK_PRESETS NewsFeatureWindowMinutes = M60; // News feature window
|
fix(signals): revive a dead MA model, and demote Sanyaku from state to event
Two defects surfaced by research/test_classic.py, both verified fixed by re-running the
transcription against 178k bars of EURUSD H1.
CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the
shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so
DiffMA(i) = a * (Close(i) - MA(i+1))
DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1))
are positive multiples of one quantity and always share a sign. Model 1 asks for a close
BELOW a RISING average, which is precisely the combination that identity forbids: 0.000%
of bars, either direction, any symbol. The MQL5 standard library this was ported from
defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA
default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for
every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars.
CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing
STATES conjoined with no transition term, so it held across long stretches - and being
last in the if-chain at the top weight, the module's highest-conviction reading was also
its most common one, overwriting all eight event models below it on a quarter of all bars.
The old comment rejected an event form because "demanding all three flip on the same bar
would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the
ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1)
fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the
strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback.
Neither pattern showed edge before or after; this is about the models meaning what they
say and the vote not being dominated by a constant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
|
|
|
//--- Cross-asset, after News for the same reason: its data also comes from outside this symbol's own
|
|
|
|
|
//--- series - in fact it is the ONLY feature here that does so without leaving the price domain. Every
|
|
|
|
|
//--- other block above, News included, is either a transform of this one instrument's OHLCV or a
|
|
|
|
|
//--- timing overlay on it. Measured end to end, that whole family sits at the noise floor
|
|
|
|
|
//--- (research/test_classic.py), which is precisely why this exists. Builds a currency-strength panel
|
|
|
|
|
//--- from the FX pairs in Market Watch and feeds the traded pair's base/quote strength plus the
|
|
|
|
|
//--- divergence between the pair and its own two currencies. Needs >= 2 usable FX pairs in Market
|
|
|
|
|
//--- Watch; degrades to a neutral 0-fill with one logged line if it cannot build, never blocks training.
|
|
|
|
|
input bool EnableCrossAsset = true; // Feature: cross-asset currency strength
|
feat(ai): spread as a volatility-regime feature, and fix a stale-index cache in both new blocks
Adds spread/ATR and the spread change ratio as network inputs (EnableSpreadFeature,
default on). Spread is the one microstructure channel that is both FX-available and
genuinely historical in the Strategy Tester - "during testing, the spread is not modeled
but is taken from historical data" - so unlike swap, signed tick flow or depth of market it
is something a backtest can honestly validate.
What it encodes, stated precisely because the raw measurement overstates it.
research/test_spread.py found spr/atr the strongest single feature in this codebase, on 5
of 8 instrument/geometry cells at 2-4x any volume feature. But the barrier LABEL charges
the spread inside its own barriers, so a wide-spread bar is mechanically likelier to
resolve as a loss and the feature would partly be predicting its own cost model. Relabelling
at zero cost and re-measuring the identical feature showed 20-40% of it WAS that tautology
and the majority was not (XAUUSD retained 97%). What survives is a volatility-regime
reading: spread is near-fixed while ATR is not, so the ratio runs high exactly when
realised volatility is below its own ATR estimate, which genuinely predicts whether
ATR-scaled barriers get reached. It is UNSIGNED - Neutral-vs-directional only, never a side.
Also fixes a stale-index bug I introduced with the cross-asset panel and had just repeated
in the spread series. Both cached on length alone:
if(m_crossAsset.Bars() >= bars) return true;
MQL5 series indices are relative to NOW, so one new closed candle shifts every index by
one. Keyed only on length, the panel keeps serving its index 0 as a bar that is no longer
the newest, and every cross-asset value is read one bar out of step with the price features
sitting beside it in the same vector - silently, with no error and no shape change. This is
the same class of defect as the dtStudied watermark behind the zero-direction backtests.
Both now carry a datetime anchor on m_Time.GetData(0), the same invalidation key the
label/feature bar caches already use.
And a performance fix that fell out of it: with correct invalidation the panel rebuilds on
every new bar, and RefreshConvergedSignal runs per bar - which in the tester would mean one
full multi-symbol resample per simulated bar at training depth. Inference only reads bars
0..m_historyBars-1 plus the panel's own slow window, so it now requests exactly that. The
cache check is >=, so a deeper panel left from training still satisfies it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:42:40 -04:00
|
|
|
//--- Spread: the only microstructure channel that is both FX-available and genuinely historical in
|
|
|
|
|
//--- the Strategy Tester, so the only one a backtest can honestly validate. Encodes a volatility
|
|
|
|
|
//--- REGIME (spread is near-fixed while ATR is not, so the ratio runs high exactly when realised
|
|
|
|
|
//--- volatility is below its ATR estimate), which predicts whether ATR-scaled barriers get reached.
|
|
|
|
|
//--- Unsigned, like volume - it informs Neutral-vs-directional and can never pick a side.
|
|
|
|
|
input bool EnableSpreadFeature = true; // Feature: spread / volatility regime
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- Searches the per-bar parameters of every ENABLED input feature above (the order-flow/Wyckoff
|
|
|
|
|
//--- MA/RSI feature periods) for the combination that trains best - see ADIndicatorTuner.mqh.
|
|
|
|
|
//--- The TRIAL COUNT IS NO LONGER AN INPUT (2026-08-01). It was a number the user had no basis to pick:
|
|
|
|
|
//--- the right budget depends on how many parameters are actually being searched and how wide each
|
|
|
|
|
//--- one's range is, both of which the code knows at runtime and the user does not. Asking for it
|
|
|
|
|
//--- guaranteed either a wasted search (too many trials on two narrow parameters) or a blind one (32
|
|
|
|
|
//--- trials against a space of millions). Now derived - see ComputeTuneTrialBudget().
|
feat: expose the AD/Wyckoff parameters; default the indicator tuner off
AutoTuneIndicators now defaults to FALSE, and the 33 AD/Wyckoff parameters
it used to search are now inputs.
WHY THE DEFAULT FLIPPED - not because the search is broken. It is correct,
and its own Sidak gate is what proves it: 324 candidates per model on
SP500 H1, "no improvement" on all four topologies (0.00236 -> 0.00236 on
the AD configs, 0.00370 -> 0.00370 on PAI), winner rejected at p=1.0000.
It cannot do better here by construction - it ranks candidates by MARGINAL
MI, and the headline MI is 0.00370 nats against a shuffled null of
0.00379 +/- 0.00061 (p=0.4975), so every candidate is a noise draw and the
maximum over N of them is noise too. The cost is 45-56 min per model in
one synchronous call with no yield, and it was the amplifier for the
handle leak fixed in 33f106d. The EA's own report says it plainest: "no
per-feature indicator retuning will help."
THE INPUT STAYS. TuneIndicatorsByFilter is one function of twelve in
AIBase/AutoTune.mqh; the other eleven are the MI/lag/excursion/geometry
diagnostics that produced every verdict this project relies on, and they
run regardless of this flag. Removing the input invites removing the file.
WHY THE INPUTS WERE NEEDED. All 33 were literals in CADIndicatorTuner's
constructor with no input of any kind, while MA/RSI/MACD/Ichimoku have had
their periods exposed from the start. On the AD configs those indicators
contribute 28 of 64 features per bar. Survivable while the tuner searched
them; indefensible with it off, where they would freeze at values nobody
chose.
CONSOLIDATED 33 -> 18. volClimax/volHigh/rangeClimax/rangeSignificant/
stVolRatio/atr were duplicated verbatim across CumulativeDelta, Wyckoff
Events, Failed Structure and Bar Inversion - the same constants restated
3-4 times. One concept, one input. They are SEEDS: each fans out to the
indicator's own struct field, so with the tuner on it retains full
per-indicator freedom to move them apart. Same contract as PeriodMA.
NO RETRAIN. Every default is byte-identical to the literal it replaces,
and the fingerprint's new ADP token is appended ONLY on deviation
(MACD/Ichimoku/BN/XA convention), gated on the AD features being enabled.
At defaults the token is absent, so every model on disk keeps its filename
and stays loadable. Without that guard, merely EXPOSING these parameters
would have re-keyed every config and forced a from-scratch retrain of all
four topologies for a change that alters no number anywhere.
All-or-nothing rather than per-input, so the token can never encode a
partial picture of what the features were built from.
Compiles clean: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 20:37:21 -04:00
|
|
|
//--- DEFAULT FLIPPED TO false 2026-08-08, and NOT because the search is broken - it is correct, and its
|
|
|
|
|
//--- own Sidak family-wise gate is what proves it. The search is simply not worth its cost on data at
|
|
|
|
|
//--- the MI noise floor, which is every instrument measured so far:
|
|
|
|
|
//--- * It scored 324 candidates per model on SP500 H1 and reported no improvement on ALL FOUR
|
|
|
|
|
//--- topologies (0.00236 -> 0.00236 on the three AD configs, 0.00370 -> 0.00370 on PAI), rejecting
|
|
|
|
|
//--- its own winner at selection p=1.0000. That is the gate working, not failing.
|
|
|
|
|
//--- * It cannot do better here by construction: it ranks candidates by MARGINAL mutual information,
|
|
|
|
|
//--- and the headline MI reads 0.00370 nats against a shuffled-label null of 0.00379 +/- 0.00061
|
|
|
|
|
//--- (p=0.4975). Every candidate is a noise draw, so the maximum over N of them is noise too, and
|
|
|
|
|
//--- the correction rejects it - more harshly the more candidates are tried.
|
|
|
|
|
//--- * The cost is not marginal: 2674s (CONV), 3271s (LSTM), 3372s (HYB) - 45 to 56 minutes per model,
|
|
|
|
|
//--- in ONE synchronous call with no yield, during which the chart is frozen and silent. It was also
|
|
|
|
|
//--- the amplifier for the indicator-handle leak (fixed, see ReInitADIndicators): 324 iterations x 6
|
|
|
|
|
//--- re-creates orphaned gigabytes and MT5 removed CONV and LSTM for running out of memory.
|
|
|
|
|
//--- Turn it ON deliberately, on an instrument whose per-feature MI actually clears its null, and expect
|
|
|
|
|
//--- to wait. The EA's own report says it plainest: "no per-feature indicator retuning will help".
|
|
|
|
|
//--- The input STAYS. The sweep is one function of twelve in AIBase\AutoTune.mqh - the other eleven are
|
|
|
|
|
//--- the MI/lag/excursion/geometry diagnostics that produced every verdict this project relies on, and
|
|
|
|
|
//--- they run on their own path regardless of this flag (see TuneIndicatorsAndTrain's else-branches).
|
2026-08-09 09:51:27 -04:00
|
|
|
input bool AutoTuneIndicators = false; // Auto-tune indicator params (slow)
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// FILTERS
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string SF_Settings = "Session Filter"; // Session Filter
|
perf(autotune): replace the genetic search with a filter score - hours to seconds
MEASURED COST OF THE GA, which is what retired it. Per generation:
rung 0: 8 cand x 3 seeds x 3 eras = 72 eras
rung 1: 4 cand x 3 seeds x 8 eras = 96
rung 2: 2 cand x 3 seeds x 20 eras = 120
= 288 eras/generation x 4 generations = 1152 eras BEFORE the winner's
real training began. Against the observed era times on SP500 H1:
PAI 29.1 s/era -> 9.3 h (matches the observed 00:37 -> 09:22)
CONV 41.3 s/era -> 13.2 h
LSTM 150.4 s/era -> 48.1 h
HYBRID 154.6 s/era -> 49.5 h
Two days to tune is not a first-run experience, and it is the phase in
which the panel goes quiet, which is what made it look like a hang.
It also bought nothing. The space is 90 points (10 MA periods x 9 MA
types), so 1152 evaluations revisited each point ~13 times; and rungs of
3 and 8 eras cannot separate two MA periods at all. The 2026-08-01 run
proves it: every finalist scored 25.0-25.9% balanced accuracy - below the
33.3% one-class floor, i.e. indistinguishable noise - and the search then
"deployed the winner" of that.
THE ERROR WAS THE SCORING FUNCTION, not its constants. Using a full
training run to choose a feature's period is a wrapper method paying
wrapper prices for a decision that does not need one. The reference book
does not do this: ch. 3.3 selects inputs by measuring each candidate
indicator's CORRELATION with the target and dropping the ones with none,
with no network involved.
So: rank candidates by the MUTUAL INFORMATION between the resulting
feature vector and the triple-barrier label. MI rather than correlation
because the label is 3-class categorical and the features are not
monotonically related to it. Equal-FREQUENCY binning (rank-based),
because these features are ATR-normalised and heavy-tailed - fixed-width
bins put nearly everything in one bucket and report ~0 information for a
genuinely useful feature.
Scoring is arithmetic over the feature cache, so it costs seconds and its
cost is independent of topology: LSTM now tunes as fast as the MLP.
Coordinate sweep, not product sweep - cost is the SUM of per-parameter
candidate counts, so enabling every indicator stays affordable - with a
second pass that breaks early once nothing moves.
Sampling is IS-ONLY. Letting the OOS window influence which indicator
settings ship would mean the holdout had been used for selection and had
stopped being a holdout.
HONEST LIMIT, recorded because it is the price: MI is marginal, so a
parameter that only pays off in combination with another can be missed
(Guyon & Elisseeff 2003, filter vs wrapper). Given the wrapper it
replaces was ranking pure noise at 48 h a run, this is strictly better.
Deleted with it: GaRungEras/GaExtract/GaStore/GaMutate/GaRandomCandidate/
GaBlockCrossover/GaSortAliveByScoreDesc/GaBreedNextGeneration, 14 m_ga*
members, the GA_*/TUNE_POP_* constants, and ComputeTuneTrialBudget.
AND m_evalMode/m_evalEraBudget, because nothing set them any more - 28
read sites all permanently inert. That is not a tidy-up: the `if
(!m_evalMode)` guard on UpdateClassPriors is exactly what silently
disabled the imbalance correction for entire runs two commits ago. Dead
machinery that still reads like live machinery is this codebase's most
expensive recurring bug, and leaving 28 more instances of it would have
been indefensible.
The panel's tuning-progress state goes too - tuning no longer takes long
enough to need one.
Both builds compile 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:29:31 -04:00
|
|
|
input bool EnableSessionFilter = false; // Signal: Session filter
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//--- All three ON by default. The filter is evaluated once per BAR (Expert_EveryTick=false ships as the
|
|
|
|
|
//--- default), so on a slow timeframe there are very few evaluations per day and a single-session
|
|
|
|
|
//--- default can starve the EA of entries entirely - on D1 there is exactly ONE evaluation, at the bar
|
|
|
|
|
//--- open, and whether that instant falls inside a narrow session window depends purely on the broker's
|
|
|
|
|
//--- server offset. Enabling all three spans 00:00-22:00 GMT so only genuinely dead hours are excluded;
|
|
|
|
|
//--- narrow it deliberately per-chart rather than inheriting it as an accident of the default.
|
|
|
|
|
input bool SF_trade_LondonSession = true; // Trade London session
|
|
|
|
|
input bool SF_trade_TokyoSession = true; // Trade Tokyo session
|
|
|
|
|
input bool SF_trade_NewYorkSession = true; // Trade New York session
|
|
|
|
|
//--- Scheduled flat-close. Deliberately its OWN group rather than part of the Session Filter above:
|
|
|
|
|
//--- CExpertCustom::OnTick() (Expert\ExpertCustom.mqh) evaluates this schedule unconditionally, so it
|
|
|
|
|
//--- fires whether EnableSessionFilter is on or off - grouping it under the session filter implied a
|
|
|
|
|
//--- coupling that has never existed in the code. Set Close-all day = Disabled to switch it off.
|
|
|
|
|
input string CA_Settings = "Scheduled Close-All"; // Scheduled Close-All
|
|
|
|
|
input CLOSE_DAY_OF_WEEK targetDayOfWeek = CLOSE_FRIDAY; // Close-all day
|
|
|
|
|
input CLOSE_HOUR_OF_DAY targetHour = CH_23; // Close-all hour
|
|
|
|
|
input CLOSE_MINUTE_OF_HOUR targetMinutes = CM_45; // Close-all minute
|
|
|
|
|
//--- INTRADAY TIME FILTER REMOVED ENTIRELY 2026-08-01 (5 inputs, plus Signals\SignalITF.mqh).
|
|
|
|
|
//--- Two of its five inputs were raw BITMASKS ("hours to avoid" as an integer), which is not a setting a
|
|
|
|
|
//--- trader can reasonably compute - it is an implementation detail exposed as a control, and it shipped
|
|
|
|
|
//--- disabled so essentially nobody ever got it right. More importantly the job is now covered three
|
|
|
|
|
//--- times over by things that learn or are declarative: the Session Filter handles "when may I trade"
|
|
|
|
|
//--- explicitly, the time-of-day/day-of-week features (EnableTime) let the NETWORK discover which hours
|
|
|
|
|
//--- are good on this instrument instead of being told, and the trade journal ranks by time bucket.
|
|
|
|
|
//--- A hand-specified hour mask is the least informed of the four and the hardest to use.
|
|
|
|
|
input string NF_Settings = "News Filter"; // News Filter
|
|
|
|
|
input bool EnableNewsFilter = true; // Signal: News filter
|
|
|
|
|
input NF_LOOKBACK_PRESETS NF_LookMinutes = M60; // News avoid window (min)
|
|
|
|
|
input NF_IMPACT_PRESETS NF_MinImpact = HOLIDAYS; // Min news impact to avoid
|
|
|
|
|
//--- MARKET DEPTH FILTER REMOVED ENTIRELY 2026-08-01 (5 inputs, plus Signals\SignalMarketDepth.mqh).
|
|
|
|
|
//--- It needs real level-2 DOM data, which this development broker does not provide and which most
|
|
|
|
|
//--- retail MT5 brokers do not provide either - so the module has never been executed against real data
|
|
|
|
|
//--- even once. Shipping four tuning dropdowns for an UNTESTED code path is worse than shipping nothing:
|
|
|
|
|
//--- the only users who could enable it are the ones whose broker supplies a book, and they would be
|
|
|
|
|
//--- the first people ever to run it, in live trading, with no validation behind it. If DOM support is
|
|
|
|
|
//--- wanted later it should return as a feature fed to the network rather than as a rule-based veto with
|
|
|
|
|
//--- its own hand-tuned thresholds - the imbalance is data, and data belongs in the input vector.
|
|
|
|
|
input string RiskGuard_Settings = "Risk Guard"; // Risk Guard
|
|
|
|
|
input bool EnableRiskGuard = true; // Signal: Risk Guard
|
2026-08-02 12:25:20 -04:00
|
|
|
//--- FREE-ENTRY PERCENTAGES, replacing the RISK_LIMIT_PCT_PRESET dropdown these two used to be
|
|
|
|
|
//--- (that enum is gone - see Enumerations\InputEnums.mqh). Every funded/prop programme sets its own
|
|
|
|
|
//--- numbers and they are not always integers, so a fixed ladder of presets could not express them;
|
|
|
|
|
//--- 4.5% or 3.75% were simply unreachable. Enter the limits from YOUR account agreement, and enter
|
|
|
|
|
//--- them slightly TIGHTER than the contract if you want margin for slippage past a stop.
|
|
|
|
|
//--- 0 disables a rule. Enforced live at quote frequency by Variables\RiskBudget.mqh - not once per
|
|
|
|
|
//--- bar, which is all the old guard could manage.
|
|
|
|
|
input double MaxDailyLossPct = 4.0; // Daily loss limit % (0 = off)
|
|
|
|
|
input double MaxDrawdownPct = 8.0; // Max total drawdown % (0 = off)
|
|
|
|
|
//--- TRUE: max drawdown is measured down from the highest equity ever reached (trailing DD, the
|
|
|
|
|
//--- stricter and more common funded-account rule). FALSE: measured from the equity this EA first
|
|
|
|
|
//--- saw on the account (static DD). Pick whichever your programme actually uses - a trailing rule
|
|
|
|
|
//--- applied to a static challenge halts trading long before it has to.
|
|
|
|
|
input bool MaxDrawdownIsTrailing = true; // Max DD trails the equity peak
|
|
|
|
|
//--- Broker-server hour at which the firm's trading day (and therefore the daily loss allowance)
|
|
|
|
|
//--- resets. Broker time here is NOT your local time; if the firm quotes the reset in another zone,
|
|
|
|
|
//--- convert it. A misaligned window hands the allowance back hours early or late.
|
|
|
|
|
input int RiskDayResetHour = 0; // Risk day reset hour (broker time, 0-23)
|
|
|
|
|
//--- Ceiling on what ONE trade may risk, as a share of the allowance that is genuinely left after
|
|
|
|
|
//--- subtracting every open position's remaining loss-to-stop. This is the fix for the real breach
|
|
|
|
|
//--- mode: without it a trade at 3.2% into a 4% day still sized for a full risk unit and a routine
|
|
|
|
|
//--- stop-out went through the limit. At the default 50% a full stop-out spends at most half of
|
|
|
|
|
//--- what is left, so even a stop that slips to twice its distance lands inside the limit.
|
|
|
|
|
input double RiskPerTradeOfBudget = 50.0; // Max % of remaining budget per trade
|
|
|
|
|
//--- Blocking new entries cannot stop an ALREADY-OPEN position from running through the limit, which
|
|
|
|
|
//--- is the way a hard daily loss rule is actually breached. Turn this on to close this EA's own
|
|
|
|
|
//--- positions (matching symbol + magic) the moment a limit is hit. Default OFF because closing
|
|
|
|
|
//--- positions is a materially bigger behaviour change than declining to open them - but leaving it
|
|
|
|
|
//--- off means the limits above are advisory, not enforced.
|
|
|
|
|
input bool RiskGuardFlatten = false; // Close own positions on breach
|
feat: expectancy stop - halt when the measured result says the strategy loses
The daily (4%) and total (8%) rules bound how FAST an account can lose. Nothing
noticed WHETHER it was losing. A negative-expectancy signal traded at 1% inside
that envelope breaches no rule and still arrives at zero - it just takes longer,
with every limit green the whole way down. That is the realistic way this EA
destroys an account, and no existing guard could see it.
THE ARITHMETIC THIS ENFORCES. Expected value per trade is p*TP - (1-p)*SL - cost.
With no directional edge p equals SL/(SL+TP), which is also the break-even rate,
so the payoff terms cancel exactly and EV = -cost. Expected P&L is -(trades) x
cost: strictly negative, proportional to activity. Measured here: directional
precision 23-24% against a 25% break-even, flat across every confidence tier,
with 58 points of spread on SP500. Sizing, stop placement and trailing move
variance around that mean; none of them changes its sign.
So every closed position now reports its result in R (net profit over money
actually at risk) and the running mean is tested against zero. Above the
configured minimum sample, if mean + sigma*SE < 0, new entries stop.
- SIGNIFICANTLY below, not merely below. A run of losers is ordinary variance
even for a profitable system; halting on the raw mean would be the same
act-on-noise error the MI gates exist to prevent. Using the standard error
means a wide spread simply demands more trades before the rule can fire.
- NET of swap and commission (ResolveClose already sums all three). Deliberate
and load-bearing: when the edge is zero, cost IS the expectancy, so a gross
version would measure a strategy nobody can trade.
- Reported in R so symbols, lot sizes and balances share one scale and one
mean. Trades without a stop are not scored rather than assigned a guessed R.
- LATCHED across restarts, like the daily halt and for the same reason: a
latch a reattach clears is not a latch. Clearing it means deleting the risk
state file, deliberately, after looking at why.
State is appended to the risk file length-guarded, so files written before this
still load and start their sample at zero rather than misreading.
Defaults 40 trades / 2 sigma; ExpectancyMinTrades = 0 disables it.
This does not make the strategy profitable and is not meant to. It stops paying
tuition on one the results say is losing, and does it on measurement rather than
on a drawdown limit finally being reached.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:20:00 -04:00
|
|
|
//--- EXPECTANCY STOP. The daily and total limits bound how FAST the account can lose; neither notices
|
|
|
|
|
//--- WHETHER it is losing. A negative-expectancy signal traded inside a 4%/8% envelope breaches no rule
|
|
|
|
|
//--- and still arrives at zero - it just takes longer. This tests the realised mean result per trade
|
|
|
|
|
//--- against zero and stops opening new positions once it is significantly below.
|
|
|
|
|
//--- Significantly, not merely below: a run of losers is ordinary variance even for a profitable system,
|
|
|
|
|
//--- so the test uses the standard error of the mean and a wide spread simply demands more trades before
|
|
|
|
|
//--- it can fire. Measured in R (net profit over money risked), so symbols and lot sizes share one scale,
|
|
|
|
|
//--- and net of swap and commission - when the directional edge is zero, cost IS the expectancy.
|
|
|
|
|
//--- 0 trades = off. The halt is LATCHED and survives a restart; clearing it means deleting the risk
|
|
|
|
|
//--- state file, deliberately, after looking at why.
|
|
|
|
|
input int ExpectancyMinTrades = 40; // Halt if losing: min closed trades first (0 = off)
|
|
|
|
|
input double ExpectancySigma = 2.0; // ...and mean must be this many std errors below zero
|
refactor(inputs): 96 -> 70 inputs; remove two untested/unusable filter modules
Every removal below is FINGERPRINT-NEUTRAL by construction: each retired
input is pinned to the exact value it already shipped with, so running
models keep their filenames and resume rather than restarting at era 0.
Verified field by field against BuildConfigFingerprint.
Removed as inputs, kept as pinned constants (the value was never a
preference the user had a basis to change):
- OutputNeuronsCount. The regression head predicts a continuous quantity
the triple-barrier label does not contain; the target is an EVENT, so
the right output is its probability. The regression code paths stay
implemented and dormant - they cost nothing and removing them would
touch every scoring path at once.
- MinRecall. A safety floor, not a preference, and the only direction a
user can move it is the harmful one: raising it past what the config
reaches yields NO model, not a better one (observed repeatedly at 60).
- SwingConfirmationBars. Stopped gating the labels with the relabel, but
is STILL load-bearing for the swing-context input features - it is the
ZigZag repainting embargo, and without it those 9 features read a leg
the live bar could not have had yet. Pinned, not deleted.
- MaxErasPerRun (runaway backstop, never reached in a healthy run),
FreezePriorCalibration (unanswerable by a user; near-balanced labels
make the priors stable anyway), VerboseMode (developer view, joins
DebuggingMode), MACD/Ichimoku periods x6 (both indicators ship
disabled, and as optimizer dimensions they are pure overfitting
surface - the AI auto-tuner is the supported way to move them).
- SignalClusterWindow -> 3, no longer an input. Barrier labels make
consecutive setups real, which argued for 0; it is not 0 because on D1+
a 6-bar window spans over a week and two arrows a day apart on a
weekly-scale move are one event. 3 splits it correctly by timeframe.
- EnableOnlineLearning -> ON. Adapting to a changing market is what keeps
a months-attached model from going stale, and the rolling-accuracy
freeze is what makes it safe. See the caveat noted in the handoff: it
had not been forward-tested on a live feed when this became default.
Removed entirely:
- Intraday Time Filter (5 inputs + Signals/SignalITF.mqh). Two of its
five inputs were raw BITMASKS, which is an implementation detail
exposed as a control. The job is covered three times over by things
that are declarative or that learn: the session filter, the
time-of-day/day-of-week input features (the network discovers which
hours are good rather than being told), and the journal's time buckets.
- Market Depth Filter (5 inputs + Signals/SignalMarketDepth.mqh, plus
its OnInit probe and OnDeinit release). It needs real level-2 data
that this broker - and most retail MT5 brokers - do not provide, so
the module has never once executed against real data. Shipping four
tuning dropdowns for an untested path is worse than shipping nothing:
the only users who could enable it would be its first-ever testers,
live. If DOM returns it should be a FEATURE fed to the network, not a
rule-based veto with hand-tuned thresholds - imbalance is data.
- IndicatorTuneTrials, replaced by ComputeTuneTrialBudget(). The useful
budget depends on how many parameters are actually being searched,
which depends on which features are enabled - so one number meant
wildly different things run to run. The shipped 32 was ~10 candidates
per dimension against one enabled indicator (wasteful: each costs
GA_SEEDS full training runs) and under one per dimension against all
nine (blind). Now population ~ 4 x active dimensions, clamped [8,64],
with CADIndicatorTuner::ActiveDimensions() defined immediately above
PerturbRandom() so the two cannot drift apart.
- Six orphaned enums (TUNE_TRIALS_PRESET, DOM_*, ENTRY_HOUR_OF_DAY,
TIME_FILTER_DAY_OF_WEEK), 81 lines.
Other UX:
- SL_ATR_x1 / TP_ATR_x3 now carry the "(classic)" default marker every
other preset enum in the file already used. Nothing in the SL/TP
dropdowns previously told a user which pair was the shipped default -
which matters far more since the relabel, because those two define the
labels and changing either forces a retrain.
- Neural Network section moved directly ABOVE AI Input Features: choose
the architecture, then choose what it sees. NN Optimizer / Performance
stays last - the Adam/Sgd inputs are declared in AI/Network.mqh and
render immediately after that divider.
- News feature + window moved to the end of the AI feature list, below
Wyckoff Bar Inversion.
- Dropped "(0-100)" from Min vote to open - it is an enum, not a number.
Both builds compile 0 errors / 0 warnings. No retrain forced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 21:22:02 -04:00
|
|
|
//==================================================================================================
|
|
|
|
|
// TRADE JOURNAL / PATTERN RANKING
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
input string Journal_Settings = "Trade Journal / Ranking"; // Trade Journal / Ranking
|
|
|
|
|
//--- Enables the per-pattern win-rate database: scales each signal's vote by its historical win rate,
|
|
|
|
|
//--- records every trade, and powers the Export Trade Journal Report button (see the control panel).
|
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
|
|
|
//--- Default TRUE since 2026-08-13 (user request): a META chart should journal + rank out of the box,
|
|
|
|
|
//--- and the classic-only configuration benefits from ranked weights as soon as history accumulates.
|
|
|
|
|
input bool UseDatabaseRanking = true; // Weight filters by DB win-rate
|
2026-08-12 15:20:33 -04:00
|
|
|
//--- Row cap per pattern table (oldest row pruned past it). 1000 is plenty for live ranking; a
|
|
|
|
|
//--- META-LABEL CORPUS BUILD (Meta_Labeling_Design.md, stage S1: a long backtest whose signal DB
|
|
|
|
|
//--- becomes the training set) needs it raised so a 15-20 year run isn't pruned away - 20000 holds
|
|
|
|
|
//--- ~3x the densest pattern's 20-year stretch count. A high cap costs nothing until rows exist.
|
|
|
|
|
input int DB_MaxRowsPerTable = 1000; // Max rows kept per pattern table
|
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
|
|
|
//--- META DATASET EXPORT (cross-sectional pooling, Meta_Labeling_Design.md). With AIType=META, the
|
|
|
|
|
//--- chart writes its full training set - every resolved candidate's feature window + setup
|
|
|
|
|
//--- descriptor + triple-barrier label - to Common\Files\Warrior_EA\MetaExport\<sym>_<period>.f32
|
|
|
|
|
//--- (float32 rows; sidecar .meta.csv carries width/geometry/BE) once per attach, then trains as
|
|
|
|
|
//--- normal. Pooled training across symbols happens OFFLINE on these files; the EA itself is
|
2026-08-13 16:31:15 -04:00
|
|
|
//--- unchanged. Costs one pass-1-sized sweep (~a minute) at attach. Default ON in the private build
|
|
|
|
|
//--- (the pooling campaign's drop-on-chart workflow); OFF for Market.
|
|
|
|
|
#ifdef WARRIOR_MARKET_BUILD
|
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
|
|
|
input bool Meta_ExportDataset = false; // META: export training dataset at attach
|
2026-08-13 16:31:15 -04:00
|
|
|
#else
|
|
|
|
|
input bool Meta_ExportDataset = true; // META: export training dataset at attach
|
|
|
|
|
#endif
|
2026-07-29 15:33:37 -04:00
|
|
|
//--- Header only. The AI\Network.mqh optimizer inputs (Adam*, Sgd*) are declared in that library
|
|
|
|
|
//--- header; because this Inputs file is included FIRST (see Warrior_EA.mq5), those
|
2026-07-25 21:32:15 -04:00
|
|
|
//--- render immediately AFTER this divider - grouping them here instead of leading the Inputs tab.
|
2026-07-22 17:17:23 -04:00
|
|
|
input string NNPerf_Settings = "NN Optimizer / Performance"; // NN Optimizer / Performance
|