Warrior_EA/Variables/Inputs.mqh
AnimateDread 8ab8cf6b92 feat(chart): on-chart vote readout, and Min_Vote_Open 50 -> 40
THRESHOLD. 40 is a measured correction, not a preference. Once
RankTiersFromOos() replaced the designed tier priors with each model's real
held-out win rate, the vote converges on that win rate - logged 2026-08-18 as
pooled 23-36% across four members on three symbols - so a 50% bar could not be
reached by anything on offer and the ensemble gate fired on 0 of 4,865 OOS
bars. 40 clears the ~34% break-even those same lines report without being
unreachable. The comment says plainly not to copy the number: break-even is a
function of the barrier geometry, so read the gate's own "needs >N%" for the
config in front of you.

READOUT. One line, top-right:

  VOTE SELL  37.2%  peak  44.1%  need 40%  3 voter(s)  -> no trade

Every other number on the chart is downstream of the weighted mean the open
threshold is compared against, and that was the one quantity never displayed.
A chart with no arrows could mean the models abstained, the vote was diluted,
or the threshold is unreachable - and telling those apart meant waiting for an
era to end and reading the gate line, which is how the last two sessions went.

PEAK is the part that earns its space. A threshold above what the vote ever
attains can never fire, and that is not knowable from a single bar - it is
precisely the "unreachable gate vs merely unmet gate" confusion this project
has paid for twice. Colour carries the verdict rather than the direction:
green/red ONLY when the vote would actually place an order, grey otherwise.
Green-for-buy would make a below-threshold buy look like a trade, which is the
specific misreading the display exists to prevent.

Guarded on `total > 0` for the same reason the normalization is: Direction()
is inherited as-is by every leaf filter, so without it each filter would write
its own opinion into the one shared label and the last to run would win - the
reader would be looking at an arbitrary member's number believing it was the
vote. Drawn after the +-100 range check, so it shows what the threshold is
actually tested against.

CORNER_RIGHT_UPPER: the status lines, control panel and ensemble panel all
live on the left. Registered in WarriorChartPrefixes() explicitly even though
the "Warrior" catch-all already reaches it - that catch-all exists because the
list has drifted twice, not to make entries optional.

NOT COMPILED - user compiles in MetaEditor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 18:22:45 -04:00

769 lines
66 KiB
MQL5

//+------------------------------------------------------------------+
//| Inputs.mqh |
//| AnimateDread |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "AnimateDread"
#property link "https://www.mql5.com"
#include "..\Enumerations\InputEnums.mqh"
//--- 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,
//--- 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.
//==================================================================================================
// 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
//--- 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;
//--- 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
//--- on belongs in a log. Flip to true and recompile when diagnosing a training run.
const bool DebuggingMode = false;
//--- 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;
//==================================================================================================
// 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
//--- 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.
//---
//--- 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.
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)
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)
//--- 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.
//--- 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.
//--- 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.
//--- BOTH THRESHOLDS ARE CONFIDENCE PERCENTAGES as of 2026-08-18 (user request: "I would like them to
//--- be confidence percentages, so the current 20 would be only 20% confidence in a profitable
//--- trade"). The vote is a WEIGHTED MEAN of the firing patterns' weights, and under
//--- UseDatabaseRanking each of those weights is that pattern's measured win rate - so 60 reads as
//--- "the patterns backing this trade won 60% of the time". See the normalization comment in
//--- CExpertSignalCustom::Direction() for why it used to be a mean of PRODUCTS of two win rates,
//--- which is what made the old default of 20 sensible: the number was not on a probability scale.
//---
//--- DEFAULT 20 -> 50 -> 40. The first move was not a tightening, just the same bar re-expressed on
//--- the new scale. The second is a MEASURED correction: once RankTiersFromOos() replaced the
//--- designed tier priors with each model's real held-out win rate, the vote converges on that win
//--- rate - logged 2026-08-18 as pooled 23-36% across four members on three symbols - so a 50%
//--- bar could not be reached by any model on offer and the gate fired on 0 of 4,865 OOS bars.
//--- 40 sits above the ~34% break-even those same lines report without being unreachable. THIS IS
//--- NOT A NUMBER TO COPY: break-even is a function of the barrier geometry, so read the "needs
//--- >N%" figure the ensemble gate prints for YOUR config and set this above it.
//--- (original note) It is not a tightening - it is the same bar
//--- re-expressed. The old 20 on the product scale corresponds to roughly a coin flip once the
//--- derating is removed, and a threshold below break-even cannot be a filter. Break-even itself is
//--- computable from the barrier geometry (the ensemble gate already prints it as "need N%"), so
//--- set this ABOVE that number, not by feel: at a 2:6 ATR stop/target break-even is 25%, at 1:1 it
//--- is 50%. 80-100 is usable and very selective - MACD's double-divergence pattern carries weight
//--- 100 by default, so a lone high-conviction classic vote can still reach the top of the scale.
input PERCENTAGE_PRESETS Min_Vote_Open = PCT_40; // Min confidence to open (%) - AI + classic
input VOTE_CLOSE_PRESETS Min_Vote_Close = VOTE_CLOSE_DISABLED; // Min opposite confidence to close (%) - AI + classic
//--- WHAT THE ARROWS ON THE CHART MEAN. Two genuinely different questions, and one switch:
//---
//--- OFF (default) - THE FILTERED VIEW: "how would the whole bot have traded". One arrow per position
//--- the EA would open, after the weighted vote is averaged across every voting filter (AI members
//--- AND enabled classic signals), after UseDatabaseRanking has re-weighted each pattern by its
//--- measured win rate, and after Min_Vote_Open. Forward of attach these are drawn at the real
//--- decision point, so they also carry the prohibition signal, the trade-direction restriction and
//--- SL/TP validation - one arrow is one order the EA would have placed. Behind attach they are
//--- RECONSTRUCTED from each model's cached per-bar decision plus a replay of the classic ladders,
//--- which reproduces vote+ranking+threshold but cannot replay a broker-side rejection.
//---
//--- ON - THE RAW VIEW: every model's own opinion, per model, ignoring the vote, the ranking and the
//--- threshold entirely. This is the pre-2026-08-18 behaviour and it is the DIAGNOSTIC view: it is
//--- how you see that one ensemble member has collapsed to Neutral or gone one-sided, which the
//--- filtered view cannot show you because a collapsed member simply stops appearing in it. Classic
//--- signals draw here too, under their own name, exactly as the AI members do.
//---
//--- Neither view is a performance measurement - most of the chart during training is in-sample, and
//--- the honest numbers are the deploy gate's OOS figures and a tester run. This switch decides which
//--- QUESTION the chart answers, not how good the answer is.
input bool DrawUnfilteredSignals = false; // Draw raw per-model signals (bypass vote/ranking/threshold)
//==================================================================================================
// CLASSIC SIGNALS (rule-based MA/RSI votes - trade alongside or instead of the neural network)
//==================================================================================================
input string Classic_Settings = "Classic Signals"; // Classic Signals
//--- ALL FOUR CLASSIC FAMILIES DEFAULT OFF 2026-08-16 (user request, alt-data campaign): the EA is
//--- AI-first, classic votes are an opt-in experiment (the user may try them in the vote later). The
//--- WARRIOR_MARKET_BUILD branches are gone with the marketplace variant (private-use pivot) - one
//--- default per flag again. History: private defaults were flipped ON 2026-08-13 as META corpus
//--- candidate sources; the sweep corpus builder still needs them ON, which is a per-chart Inputs-tab
//--- choice on a META chart, not a shipping default.
input bool EnableMA = false; // MA classic vote
input bool EnableRSI = false; // RSI classic vote
input bool EnableMACD = false; // MACD classic vote
input bool EnableIchimoku = false; // Ichimoku classic vote
//--- MA/RSI PERIODS ARE NO LONGER INPUTS (2026-08-16) - same treatment MACD/Ichimoku got 2026-08-01
//--- and the AD/Wyckoff block got earlier today, closing the set: ALL indicator parameters are now
//--- tuner-owned. These constants are only the SEED; the auto-tuner searches from them (gated), and
//--- the adopted values persist chart-level in TunedPeriods_{SYM}_{TF}.cfg (Variables\TunedPeriods.mqh)
//--- which BOTH consumers read at init - the classic votes and the AI features - so the two can never
//--- run different periods for the same concept. An operator who must hand-set a period edits these
//--- constants (deliberate speed bump: hand-set values bypass the tuner's family-wise gate).
const MA_PERIOD_PRESETS PeriodMA = MA_PERIOD_50; // MA period seed
const MA_TYPE_PRESETS MA_Type = MA_TYPE_SMA; // MA type seed
const RSI_PERIOD_PRESETS PeriodRSI = RSI_PERIOD_14; // RSI period seed
//--- 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;
//==================================================================================================
// NEURAL NETWORK (training)
//==================================================================================================
input string NNetworks_Settings = "Neural Network"; // Neural Network
//--- 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:
//--- - WARRIOR_MARKET_BUILD defined (Market submission): OFF - a fresh install trades from Classic
//--- 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.
#ifdef WARRIOR_MARKET_BUILD
input AI_CHOICE AIType = AI_NONE; // AI architecture preset (or Disabled)
#else
//--- AI_HYBRID is the ENSEMBLE preset since 2026-08-15 (see AI_CHOICE): all four direction NNs
//--- train, self-gate and vote on the one chart - the drop-on-D1-chart fractal campaign gets every
//--- topology's verdict from a single attach, and only gate-certified members ever vote. Solo
//--- architectures remain selectable per-run as always. Cost note: four nets train per chart, so on
//--- sub-daily timeframes prefer a solo preset unless the machine budget allows it.
input AI_CHOICE AIType = AI_HYBRID; // AI architecture preset (or Disabled)
#endif
//--- Training target for the direction models - see TRAINING_TARGET's declaration comment.
//---
//--- 2026-08-16: the private build's default returns to TARGET_BARRIER. It had pointed at
//--- TARGET_FRACTAL for the 2026-08-15 campaign (predict the next confirmed swing extreme's
//--- direction - the reference library's per-bar target, chosen for its ~balanced classes). That
//--- campaign was ADJUDICATED DEAD the following day: 5,700 model-eras flat at -2pp, best-of-243
//--- p=0.17. The default was never flipped back, so every fresh private-build attach kept training
//--- a target already known to carry nothing - a live trap, since nothing in the run says so.
//---
//--- The campaign also had a second cost that only surfaced when the collapse was traced. Choosing
//--- the fractal target FIXED the Buy/Sell balance (48.3 / 41.1 measured) and, unnoticed, made
//--- Neutral a thin 10.6% residual - so the class-imbalance correction, built when Neutral was the
//--- 94% majority, began subsidising it by 1.20 logits and the model collapsed onto it. See
//--- ApplyLogitAdjustment. Under the barrier target the same geometry (stop 0.62 / target 1.18)
//--- gives roughly 34/34/31, where Neutral is neither rare nor dominant and the correction is close
//--- to a no-op - which is the right answer when there is nothing to correct.
//---
//--- THE INPUT IS WITHDRAWN, not merely re-defaulted (user, 2026-08-16: "remove the option if there
//--- is only one choice for now"). With the fractal campaign closed there is exactly one live target,
//--- and an input offering a single real choice is worse than no input: it presents a dead option as
//--- a supported one, and every operator who picks it silently trains a model already adjudicated to
//--- carry nothing. The triple-barrier label is now unconditional for direction models.
//---
//--- TO RE-ENABLE for a rerun of the campaign, three lines come back: this input (the TRAINING_TARGET
//--- enum is deliberately KEPT in Enumerations\InputEnums.mqh for exactly that), the
//--- TrainTargetFractal() call in Warrior_EA.mq5's signal setup, and the HoldToBarrier() exit-policy
//--- block further down it. Nothing else was deleted - the label itself, its |TGT:FRA1 fingerprint
//--- token, its conditional barrier-geometry derivation and the campaign's trained models are all
//--- still on disk and still correct, so a rerun is a re-enable rather than a rebuild.
//--- SGD or ADAM weight update (honored by PAI/CONV/LSTM/HYBRID). SGD rate/momentum are AI\Network.mqh inputs.
//--- 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
//--- 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;
//--- 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.
//--- 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().
//--- 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.
//--- 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.
//--- 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.
//--- 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
//--- 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.
//--- 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)
//--- 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.
input OOS_SPLIT_PRESET OOSSplit = OOS_30; // Out-of-sample holdout
//--- 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%.
//--- 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.
//--- 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;
//--- 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.
//==================================================================================================
// 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)
//--- 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.
//--- 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;
//--- 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;
//--- 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_10000;
//==================================================================================================
// AI INPUT FEATURES (the data the neural network sees each bar)
//==================================================================================================
input string AISignals = "AI Input Features"; // AI Input Features
//--- 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
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
//--- ORDER-FLOW/WYCKOFF FEATURES DEFAULT OFF 2026-08-16 (user request): the alt-data campaign makes
//--- externally-measured features the default information diet; the Wyckoff stack (28-36 features/bar)
//--- is opt-in per chart. The toggles stay - Wyckoff CONTEXT is the one price-derived family that ever
//--- replicated out of sample - but a shipping default should carry the feature set with measured
//--- incremental value, and today that is price basics + alt data.
input bool EnableADCumulativeDelta = false; // Feature: Cumulative Delta
input bool EnableADShorteningOfThrust = false; // Feature: Shortening of Thrust
input bool EnableADWyckoffEventStream = false; // Feature: Wyckoff Events
input bool EnableADWyckoffFailedStructure = false; // Feature: Wyckoff Failed Structure
input bool EnableADWyckoffSignificantBarInversion = false; // Feature: Wyckoff Bar Inversion
//==================================================================================================
// 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
//--- PRUNED FROM THE MENU 2026-08-16 (user request: smaller input list, tuner-owned values). The 18
//--- inputs that lived here for eight days become compile-time aliases of their own defaults, so every
//--- consumer (CADIndicatorTuner's seeds, ConfigFingerprint's ADP token) is untouched and the values are
//--- byte-identical to what the menu shipped. The OPERATOR path to these numbers is now the auto-tuner:
//--- it defaults ON (see AutoTuneIndicators below), searches from these seeds under a family-wise gate,
//--- and persists winners inside the .nnw next to the weights. Anyone who genuinely needs to hand-set a
//--- value edits the _DEF constant above - a deliberate speed bump, because hand-set values bypass the
//--- gate that keeps noise out of the feature stack.
#define Wyk_VolClimaxMult WYK_VOL_CLIMAX_DEF
#define Wyk_VolHighMult WYK_VOL_HIGH_DEF
#define Wyk_RangeClimaxMult WYK_RANGE_CLIMAX_DEF
#define Wyk_RangeSignificantMult WYK_RANGE_SIGNIF_DEF
#define Wyk_ShortTermVolRatio WYK_ST_VOL_RATIO_DEF
#define Wyk_AtrMult WYK_ATR_MULT_DEF
#define ADCD_Lookback ADCD_LOOKBACK_DEF
#define SOT_ThrustLookback SOT_THRUST_LOOKBACK_DEF
#define SOT_MinImpulses SOT_MIN_IMPULSES_DEF
#define SOT_Threshold SOT_THRESHOLD_DEF
#define WES_Lookback WES_LOOKBACK_DEF
#define WES_ZigZag WES_ZIGZAG_DEF
#define WES_TouchATR WES_TOUCH_ATR_DEF
#define WES_ARMinATR WES_AR_MIN_ATR_DEF
#define WES_MaxRangeBars WES_MAX_RANGE_BARS_DEF
#define WFS_Lookback WFS_LOOKBACK_DEF
#define WFS_ZigZagStrength WFS_ZIGZAG_STRENGTH_DEF
#define WSBI_Lookback WSBI_LOOKBACK_DEF
//--- 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
//--- 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
//--- 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
//--- Alternative data: the externally-collected, publication-stamped block (COT positioning, VIX
//--- complex, macro) - the only feature family here whose information does not exist anywhere in the
//--- terminal. Per-symbol feature sets are decided by the research screens (family-wise + incremental
//--- gates, research/altdata/DESIGN.md) and served/maintained per System\AltDataFetch.mqh; this toggle
//--- only gates CONSUMPTION. With it on and no file for this symbol, the block contributes 0 features
//--- and the topology is unchanged - so it is safe ON everywhere. Turning it OFF on a model trained
//--- WITH alt features is a config change (input width shrinks) and correctly starts a fresh model.
input bool EnableAltData = true; // Feature: alternative data (COT / VIX / macro)
//--- API keys travel WITH the EA as input defaults so wiping Common\Files\Warrior_EA (the usual
//--- start-fresh ritual) cannot silently kill a source again (the 2026-08-16 incident: COT
//--- fetched, FRED skipped keyless, feature files never built). A keys.txt in the AltData folder
//--- is only consulted if an input is blanked. COT needs no key. EIA feeds the exploratory
//--- petroleum block on every catalog symbol (user directive; screened null on WTI, so the
//--- deploy gate - not the screen - decides whether models trained on it trade).
input string FredApiKey = "9640c07ff6574c1c23a17393b735fd36"; // FRED API key (VIX/USD features)
input string EiaApiKey = "oeSZu7EaZxG5Icjm6q78yUIXaH2EKGhIwVsdTj76"; // EIA API key (petroleum features)
//--- 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().
//--- DEFAULT HISTORY, kept because each flip was measured, not vibed:
//--- * Flipped to false 2026-08-08: the sweep scored candidates against the BARRIER (direction)
//--- label, whose headline MI read 0.00370 nats against a null of 0.00379 +/- 0.00061 (p=0.4975).
//--- Every candidate was a noise draw, the Sidak gate rejected every winner (p=1.0000 after 324
//--- candidates), and 45-56 min per model bought nothing. That was the gate working - on a target
//--- with nothing to find.
//--- * FLIPPED BACK TO true 2026-08-16, because the OBJECTIVE changed, not the gate: the sweep now
//--- scores against the excursion RANGE target (MI_TUNE_TARGET), which carries measured signal
//--- (4x its null, p=0.005, with a working positive control) - the landscape has a slope, so the
//--- search finally has something to climb. Simultaneously the 18 AD/Wyckoff menu inputs were
//--- pruned to constants (see above), making the tuner the ONLY path by which those values move -
//--- off would mean frozen-at-default forever. Winners still need the family-wise gate; a noise
//--- instrument still correctly tunes nothing.
//--- The sweep is one function of twelve in AIBase\AutoTune.mqh - the other eleven are the MI/lag/
//--- excursion/geometry diagnostics behind every verdict this project relies on, and they run on their
//--- own path regardless of this flag (see TuneIndicatorsAndTrain's else-branches).
input bool AutoTuneIndicators = true; // Auto-tune indicator params (gated, era 0)
//==================================================================================================
// FILTERS
//==================================================================================================
input string SF_Settings = "Session Filter"; // Session Filter
input bool EnableSessionFilter = false; // Signal: Session filter
//--- 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
//--- 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
//--- 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
//==================================================================================================
// 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).
//--- 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
//--- 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 = 1000000; // Max rows kept per pattern table
//--- 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
//--- 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
input bool Meta_ExportDataset = false; // META: export training dataset at attach
#else
input bool Meta_ExportDataset = true; // META: export training dataset at attach
#endif
//--- 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
//--- render immediately AFTER this divider - grouping them here instead of leading the Inputs tab.
input string NNPerf_Settings = "NN Optimizer / Performance"; // NN Optimizer / Performance
//--- Chart-level tuned-period state rides with the inputs (guarded, so the explicit include in
//--- Warrior_EA.mq5 stays harmless): every translation unit that sees the seed constants above also
//--- sees the g_Tuned* globals that supersede them - the tuner ctor reads those.
#include "TunedPeriods.mqh"