Warrior_EA/Variables/Inputs.mqh
AnimateDread 70cdec2717 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

374 lines
32 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,
//--- AI Input Features, Filters, Neural Network.
//==================================================================================================
// 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
input bool VerboseMode = false; // Detailed panel + verbose journal (off = simple panel)
//==================================================================================================
// 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
input ENTRY_MULTIPLIER Entry_Multiplier = MARKET; // Entry type/offset
input STOP_LOSS_MODE SL_Mode = SL_ATR_x1; // Stop-loss mode
input TAKE_PROFIT_MODE TP_Mode = TP_PREV_SWING; // Take-profit mode
input RISK_REWARD_RATIO Min_Risk_Reward_Ratio = RR_1x2; // Min reward:risk (reject only)
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.
input PERCENTAGE_PRESETS Min_Vote_Open = PCT_20; // Min vote to open - AI + classic (0-100)
input VOTE_CLOSE_PRESETS Min_Vote_Close = VOTE_CLOSE_DISABLED; // Min opposite vote to close - AI + classic
//==================================================================================================
// CLASSIC SIGNALS (rule-based MA/RSI votes - trade alongside or instead of the neural network)
//==================================================================================================
input string Classic_Settings = "Classic Signals"; // Classic Signals
//--- 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
//--- from the Inputs tab without recompiling.
#ifdef WARRIOR_MARKET_BUILD
input bool EnableMA = true; // MA classic vote
#else
input bool EnableMA = false; // MA classic vote
#endif
#ifdef WARRIOR_MARKET_BUILD
input bool EnableRSI = true; // RSI classic vote
#else
input bool EnableRSI = false; // RSI classic vote
#endif
//--- MACD and Ichimoku votes. Unlike EnableMA/EnableRSI above these default OFF in BOTH builds,
//--- including the Market one: they are additive to a classic set that already trades out of the box
//--- there, and turning them on by default would silently change the shipped strategy's behaviour rather
//--- than merely widening the choice. MACD contributes a momentum/divergence model (MA reads level, RSI
//--- reads a bounded oscillator - neither carries divergence); Ichimoku contributes multi-timescale
//--- support/resistance structure. Enable per-run from the Inputs tab like any other signal.
input bool EnableMACD = false; // MACD classic vote
input bool EnableIchimoku = false; // Ichimoku classic vote
//--- 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.
input MA_PERIOD_PRESETS PeriodMA = MA_PERIOD_50; // MA period
input MA_TYPE_PRESETS MA_Type = MA_TYPE_EMA; // MA type (SMA/EMA/.../T3/Kalman)
input RSI_PERIOD_PRESETS PeriodRSI = RSI_PERIOD_14; // RSI period
//--- Every fast/slow pair from these dropdowns is legal by construction (fast tops out below the lowest
//--- slow), so no combination can fail CSignalMACD::ValidationSettings() - see InputEnums.mqh.
input MACD_FAST_PRESETS MACD_PeriodFast = MACD_FAST_12; // MACD fast EMA period
input MACD_SLOW_PRESETS MACD_PeriodSlow = MACD_SLOW_26; // MACD slow EMA period
input MACD_SIGNAL_PRESETS MACD_PeriodSignal = MACD_SIGNAL_9; // MACD signal period
//--- Same all-combinations-legal design against Tenkan < Kijun < Senkou B.
input ICHIMOKU_TENKAN_PRESETS Ichimoku_PeriodTenkan = ICHI_TENKAN_9; // Ichimoku Tenkan-sen period
input ICHIMOKU_KIJUN_PRESETS Ichimoku_PeriodKijun = ICHI_KIJUN_26; // Ichimoku Kijun-sen period
input ICHIMOKU_SENKOU_PRESETS Ichimoku_PeriodSenkou = ICHI_SENKOU_52; // Ichimoku Senkou Span B period
//==================================================================================================
// AI INPUT FEATURES (the data the neural network sees each bar)
//==================================================================================================
input string AISignals = "AI Input Features"; // AI Input Features
//--- ind_Periods is the number of bars per input sequence fed to the network (and the ATR lookback).
//--- Must stay >= ADZigZag Depth (12) so a full swing leg is visible to the model.
input IND_PERIODS_PRESETS ind_Periods = PERIOD_20; // Bars to analyse
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 = false; // 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 (same repainting embargo as labels).
input bool EnableSwingContext = true; // Feature: ZigZag swing context
//--- News event proximity/impact only (not actual-vs-forecast, which isn't knowable ahead of time).
input bool EnableNews = false; // Feature: news proximity
input NF_LOOKBACK_PRESETS NewsFeatureWindowMinutes = M60; // News feature window
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
//--- 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.
input bool AutoTuneIndicators = true; // Auto-tune indicator params
input TUNE_TRIALS_PRESET IndicatorTuneTrials = TT_32; // Auto-tune trials
//==================================================================================================
// FILTERS
//==================================================================================================
input string SF_Settings = "Session Filter"; // Session Filter
input bool EnableSessionFilter = true; // 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
//--- Own divider so the hours/days filter doesn't render under the Close-All group above (before that
//--- group existed it sat under the Session Filter header, which was equally misleading).
input string ITF_Settings = "Intraday Time Filter"; // Intraday Time Filter
input bool EnableITF = false; // Signal: Intraday time filter
input ENTRY_HOUR_OF_DAY ITF_GoodHourOfDay = -1; // Preferred hour
input int ITF_BadHoursOfDay = 0; // Hours to avoid (bitmask)
input TIME_FILTER_DAY_OF_WEEK ITF_GoodDayOfWeek = -1; // Preferred day
input int ITF_BadDaysOfWeek = 0; // Days to avoid (bitmask)
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
input string DOM_Settings = "Market Depth Filter"; // Market Depth Filter
//--- Live-only rule-based confirmation/veto (needs real broker DOM); verified once in OnInit().
input bool EnableMarketDepth = false; // DOM imbalance filter (live)
input DOM_DEPTH_LEVELS_PRESET DOM_DepthLevels = DOM_LEVELS_5; // DOM book levels/side
input DOM_IMBALANCE_SCALE_PRESET DOM_ImbalanceScale = DOM_SCALE_100; // DOM imbalance influence
input DOM_MAX_SPREAD_MULTIPLE_PRESET DOM_MaxSpreadMultiple = DOM_SPREADMULT_3x; // DOM max spread multiple
input string RiskGuard_Settings = "Risk Guard"; // Risk Guard
input bool EnableRiskGuard = true; // Signal: Risk Guard
//--- Blocks new entries only (never closes positions). 0 = disabled.
input RISK_LIMIT_PCT_PRESET MaxDailyLossPct = RISK_LIMIT_2; // Max daily loss % (halt entries)
input RISK_LIMIT_PCT_PRESET MaxDrawdownPct = RISK_LIMIT_15; // Max drawdown % (halt entries)
//==================================================================================================
// 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).
input bool UseDatabaseRanking = false; // Weight filters by DB win-rate
//==================================================================================================
// 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
input AI_CHOICE AIType = HYBRID_2L; // AI architecture preset (or Disabled)
#endif
//--- 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
input OUTPUT_NEURONS_COUNT OutputNeuronsCount = OUTPUT_CLASSIFICATION; // Output type
//--- 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.
//--- LSTM only - its own recurrent hidden-unit count, independent of the AI preset (which already
//--- selects the dense taper stacked after the LSTM layer). Used by LSTM and the fused HYBRID model;
//--- no effect on plain MLP/CONV.
input LSTM_HIDDEN_SIZE_PRESET LstmHiddenSize = LSTM_HIDDEN_32; // LSTM hidden size
//--- CONV only - its own convolutional output-filter count, independent of the AI preset (which already
//--- selects the dense taper stacked after the Conv+Pool stage). Used by CONV and the fused HYBRID
//--- model; no effect on plain MLP/LSTM.
input CONV_FILTER_COUNT_PRESET ConvFilterCount = CONV_FILTERS_16; // CONV filter count
//--- Shared Conv pooling span/stride for both CONV and the fused HYBRID model. Kept separate from
//--- ConvFilterCount so the feature-bank width and the downsampling geometry can be tuned independently.
//--- 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.
input 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.
input int BatchNormWindow = 1000; // AI: batch-norm window (samples)
input TRAINING_YEARS_PRESET StudyPeriods = YEARS_10; // Training years
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.
input PERCENTAGE_PRESETS MinRecall = PCT_40; // Min per-class OOS recall %
//--- 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.
//--- Post-hoc prior correction for the 3-class AI decision: the network trains on class-balance-
//--- oversampled data so its raw output over-calls the rare Buy/Sell classes; this re-weights each class
//--- by its true base rate at inference so live trading fires at (and performs like) the calibrated rate
//--- shown during training. See LOGIT_PRIOR_STRENGTH_PRESETS. 0=off (raw), 100=full calibration.
input LOGIT_PRIOR_STRENGTH_PRESETS AILogitPriorStrength = LOGIT_PRIOR_25; // AI: prior correction to true base rate
//--- Precision/recall dial for training. Fraction of full class parity the minority (Buy/Sell)
//--- oversampling targets: lower = fewer, higher-precision directional calls (Neutral kept heavier),
//--- higher (toward 100%) = more calls / higher recall / more over-calling. Watch MinRecall: too low
//--- and the model can't meet the per-class recall gate. 70% is the tuned default; try 50-60% to cut
//--- Buy/Sell over-calling. NOTE this shapes the RAW model - live calls are also base-rate-calibrated
//--- by AILogitPriorStrength above, so judge over-calling by the live-fired precision line, not raw counts.
//--- 2026-07-29: 60 -> 90. 60 overcorrected. It was lowered to cut low-precision Buy/Sell over-calling
//--- and it did - too far: the SP500 H1 runs now START Neutral-dominant at era 1 (Buy 0-11% recall) and
//--- decay from there, calling Buy/Sell on 0-4% of bars when the true directional base rate is ~6%.
//--- That is UNDER-calling, and there is no headroom left to converge down from. Contrast the run that
//--- actually deployed (28th, best balanced 66.0%): it began at Buy 90% / Sell 36% recall, 24% of bars
//--- called directionally, and settled INTO the floor from above. Over-calling in the raw model is the
//--- intended starting condition here - the note below is the reason it is safe.
//--- LOGIT-ADJUSTED LOSS (Menon et al. 2021, ICLR, "Long-tail learning via logit adjustment").
//--- Adds tau*log(prior_c) to each class logit inside the TRAINING gradient only. Minimizing softmax
//--- cross-entropy on adjusted logits is consistent for BALANCED error - the exact metric checkpoint
//--- selection already ranks on - so for the first time the loss optimizes the same thing the deploy
//--- decision does. When on, it REPLACES two other mechanisms rather than stacking with them:
//--- - minority replay is disabled (see EnableMinorityReplay). Replay duplicated rare bars up to 28x,
//--- which made Buy and Sell compete for the same replicated capacity; measured 2026-07-29 across
//--- six topologies, every model took ONE direction to ~50% recall and abandoned the other, and the
//--- direction was arbitrary (the batch-norm control went Buy 1% / Sell 42%, the exact inverse of
//--- the other five). One era in 1,301 cleared the per-class recall floor.
//--- - the post-hoc inference prior (AILogitPriorStrength) is forced off, because the offsets are
//--- already trained in - applying it again would correct for the same base rate twice.
//--- Turning this OFF restores the previous replay + post-hoc behaviour exactly.
input bool EnableLogitAdjustedLoss = true; // AI: logit-adjusted loss (replaces oversampling)
//--- tau. 100% = full tau=1.0, the paper's default and the only value carrying the consistency
//--- guarantee; lower trades balanced accuracy back toward raw accuracy. Percent, divided by 100.
input LOGIT_PRIOR_STRENGTH_PRESETS LogitAdjustTau = LOGIT_PRIOR_100; // AI: logit-adjust strength (tau)
input PERCENTAGE_PRESETS OversampleParity = PCT_90; // AI: oversample parity (lower=fewer Buy/Sell calls)
input FOCAL_GAMMA_PRESET FocalLossGamma = FG_10; // Focal-loss gamma
input bool EnableMinorityReplay = true; // AI: replay minority bars through pass-2 oversampling (off = plain one-pass training)
input bool ConstrainReplay = true; // AI: cap replay aggression and use softer replay-only focal weighting
input bool FreezePriorCalibration = false; // AI: freeze class-prior updates once the first real prior is measured
input bool UseStaticPrior = false; // AI: never update priors mid-run; keep the first measured prior distribution fixed
input SWING_CONFIRMATION_PRESET SwingConfirmationBars = SC_100; // Swing confirm bars (label)
//--- Continual learning: after the model is deployed (converged/1000-era deploy) keep adapting it on a
//--- LIVE chart to newly-CONFIRMED market structure - the same supervised ZigZag task it was trained on,
//--- waiting the full SwingConfirmationBars delay so a still-repainting recent bar is never learned. The
//--- deployed model only moves toward the update while a rolling-accuracy guardrail holds. No effect in
//--- the Strategy Tester/optimizer (the model is held fixed there); forward-test it on a demo account.
//--- The class-imbalance gap that made this a single-class drift vector is FIXED as of 2026-07-28:
//--- OnlineLearnStep() previously backpropped the raw live distribution (~94% Neutral on SP500 H1) with
//--- an unweighted sampleWeight of 1.0, which actively pulled a balanced, converged model back toward
//--- Neutral. It now applies alpha-balanced focal loss (Lin et al. 2017) driven by the measured class
//--- priors and the same OversampleParity/ConstrainReplay/FocalLossGamma inputs training uses, and pins
//--- its own reduced learning rate. See the ONLINE_LEARN_* block in Expert\ExpertSignalAIBase.mqh.
//--- Still defaults OFF: the mechanism is complete but has never been forward-tested on a live feed, and
//--- this adapts a DEPLOYED model. Run it on demo first, watch the rolling-accuracy guardrail line in
//--- the journal, then flip this on.
input bool EnableOnlineLearning = false; // AI: keep learning live from confirmed bars (demo-test first)
input int SignalClusterWindow = 6; // Signal decluster window (bars, 0=off)
input MAX_ERAS_PRESET MaxErasPerRun = ME_1000; // Max eras before prompt
//--- 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