Warrior_EA/Variables/Inputs.mqh
AnimateDread 9a0d063da4 fix(inputs): the Neural Networks group header was singular
Typo, and more wrong than it was: the group now holds four independent NN toggles rather
than one architecture selector.

NOT COMPILED - user compiles in MetaEditor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 14:23:37 -04:00

849 lines
72 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
//--- AN INPUT AGAIN (2026-08-19). Demoted 2026-08-01 for the marketplace ("a buyer does not care
//--- which plateau stage the ladder is on") - but the marketplace track was dropped on 2026-08-16
//--- and the operator IS the developer now. It returns as an input because it also gained a second
//--- job: the per-era training journal is THROTTLED when this is false. Measured 2026-08-19: the
//--- settled diagnostics (the ~2KB era line, excursion verdict, tier re-rank, calibration move,
//--- barrier hold, census) printed EVERY era for EVERY member - 22MB of journal in 9.5 hours -
//--- long after the systems they watch were confirmed working. false = each per-era diagnostic
//--- prints on the first eras and then every TRAIN_LOG_EVERY_ERAS-th (state CHANGES - new bests,
//--- stage transitions, deploy verdicts, restores, warnings, errors - always print). true = the
//--- old one-line-per-era-per-member firehose for when something needs diagnosing again,
//--- flippable live without a recompile. Also still selects the detailed on-chart panel.
input bool VerboseMode = false; // Verbose journal + detailed panel (full per-era logs)
//--- 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
//--- Intelligent (2026-08-19): the EA measures the drift itself and trades only the side(s) it
//--- supports. The label cache's Buy/Sell shares ARE the win rates of taking every bar long/short
//--- at the adopted stop/target with spread charged - their gap is the drift at this exact
//--- geometry (2026-08-19 telemetry: always-long 34.3% vs always-short 29.5% on SP500 H4, while
//--- the models leaned SHORT - fighting the one structural edge in the window). The verdict
//--- re-derives whenever the label cache rebuilds, prints only when it changes, and fails open to
//--- BOTH (unmeasured, insignificant gap, or a weaker side that still clears break-even - drift
//--- tilt alone is not a reason to refuse a profitable side). Applies to live entries, the
//--- reconstructed chart arrows, and the vote HUD's TRADE verdict identically.
//--- DEFAULT IS INTELLIGENT since 2026-08-19 (user request). Measuring the drift and then not
//--- acting on it is the worst of both: the verdict prints, looks authoritative, and gates
//--- nothing - which is exactly what happened on the first ensemble run under this build.
//--- Safe as a default because the verdict FAILS OPEN to BOTH: it drops a side only when the
//--- gap clears 2 combined SEs on the overlap-deflated sample AND the weaker side is below
//--- cost-adjusted break-even, so an instrument with no measurable drift trades exactly as it
//--- did before. Not in any fingerprint or DB key (verified): this is trade POLICY, not part of
//--- the label definition, so changing it never re-keys a model or resets training.
input TRADING_DIRECTION tradingdirection = DIRECTION_INTELLIGENT; // 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.
//--- DEFAULT 40 -> 25 (2026-08-19), WITH THE ENUM GAINING 5-POINT STEPS BELOW 50. The 40 above
//--- was priced under UNION arithmetic, where the divisor was the voters alone and the vote read
//--- the pooled win rate the moment ANY member fired. CONSENSUS re-priced the whole scale: the
//--- divisor is now every capable member, so the pooled rate (~29 on current models) is what
//--- UNANIMITY reads - the ceiling - and 40 was unreachable by construction. That is the same
//--- fired-on-0-of-4,865-bars defect the 50 -> 40 move fixed once already, reintroduced by the
//--- arithmetic change. 25 demands ~85% of the current ceiling: net near-unanimity at today's
//--- measured skill, and it scales on its own as pooled rates move because the vote is priced in
//--- win-rate money. STILL not a number to copy - the rungs (unanimity ~29, 3-of-4 ~22, 2-of-4
//--- ~14.5) shift with every tier re-rank, so read the peak on the vote HUD and the ensemble
//--- gate's "need N%" line for YOUR config and place this between the rungs you mean to require.
input PERCENTAGE_PRESETS Min_Vote_Open = PCT_25; // 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 Networks"; // Neural Networks
//--- PER-NN TOGGLES (2026-08-19, user request), replacing the AI_CHOICE preset selector: each
//--- direction NN gets its own on/off input exactly like the classic votes above, and the ensemble
//--- arithmetic adapts to whatever subset is enabled - the consensus divisor is the enabled members'
//--- capable weight, so nothing anywhere assumes "four". Two or more enabled = an ensemble (the
//--- |ENS1 fingerprint token and the joint vote-level deploy gate arm exactly as the old AI_HYBRID
//--- preset did, so existing ensemble weight files keep loading); exactly one = the old solo preset,
//--- same fingerprint, same files; none = classic signals only. The old selector could not express
//--- 2-3 member subsets, and made META mutually exclusive with the direction NNs - which is what
//--- the meta-labeling input below un-couples.
//--- Defaults per build, same WARRIOR_MARKET_BUILD rule as before: a Market submission must trade
//--- classic out of the box within MQL5's automated test window; the private build runs the full
//--- ensemble from the first attach. Cost note: every enabled NN trains a net per chart, so on
//--- sub-daily timeframes prefer fewer members unless the machine budget allows it.
#ifdef WARRIOR_MARKET_BUILD
input bool Use_MLP = false; // NN vote: MLP (dense)
input bool Use_CONV = false; // NN vote: CONV (convolutional)
input bool Use_LSTM = false; // NN vote: LSTM (recurrent)
input bool Use_CONVLSTM = false; // NN vote: CONVLSTM (conv front-end + LSTM)
#else
input bool Use_MLP = true; // NN vote: MLP (dense)
input bool Use_CONV = true; // NN vote: CONV (convolutional)
input bool Use_LSTM = true; // NN vote: LSTM (recurrent)
input bool Use_CONVLSTM = true; // NN vote: CONVLSTM (conv front-end + LSTM)
#endif
//--- META-LABELING GATE (S3 of Meta_Labeling_Design.md, wired 2026-08-19 on the user's design:
//--- "the META NN should be integrated into the voting decision pipeline"). The meta head trains
//--- exactly as before - "given a candidate that fired here, does ITS trade reach target before
//--- stop, net of cost" over the classic-candidate corpus - but instead of being a solo, vote-less
//--- preset it now runs BESIDE the direction NNs and, once its own training completes, VETOES
//--- vote-cleared entries whose predicted win probability sits below the cost-adjusted break-even.
//--- Whatever NN subset is enabled is what forms the vote, so meta-labeling applies to the enabled
//--- members by construction. It never votes a direction, never dilutes the consensus divisor, and
//--- never blocks an exit - entries only. Fail-open by design: not yet trained / no corpus / window
//--- unavailable all mean "no gate", loudly, never a silent block. The ensemble deploy gate replays
//--- this same veto over its OOS fired bars, so what gets certified is what actually trades; a SOLO
//--- direction chart's own gate does NOT model the veto (documented gap, same class as the standing
//--- solo-gate caveat). Corpus note: the head needs candidates - enable at least one classic vote
//--- (on-chart sweep) or provide a journaled signal DB, or it trains on nothing and the gate never
//--- arms.
input bool Use_MetaLabeling = false; // Meta-labeling gate on NN vote entries
//--- One roster string for logs and the trade journal's filterID column - lives here (not in
//--- Warrior_EA.mq5) because Database\TradeJournalManager.mqh is included before Variables.mqh's
//--- globals and needs it too. One arithmetic, every consumer.
string EnabledNNSummary()
{
string s = "";
if(Use_MLP)
s += (StringLen(s) > 0 ? "+MLP" : "MLP");
if(Use_CONV)
s += (StringLen(s) > 0 ? "+CONV" : "CONV");
if(Use_LSTM)
s += (StringLen(s) > 0 ? "+LSTM" : "LSTM");
if(Use_CONVLSTM)
s += (StringLen(s) > 0 ? "+CONVLSTM" : "CONVLSTM");
if(StringLen(s) <= 0)
s = "Classic";
if(Use_MetaLabeling)
s += "+metaGate";
return s;
}
//--- 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 front-end
//--- 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
//--- "Market close" (the last hour option) resolves per day from the symbol's own session table
//--- and backs off "Close-all minute" minutes: day Friday + hour Market close + minute xxH05 =
//--- flatten 5 minutes before Friday's actual close, DST-proof and per-symbol. It is also where
//--- the LABEL walk stops treating a trade as holdable (Expert\AIBase\Labels.mqh) - one
//--- definition of "the day ends" for the live book and training alike.
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 the meta head
//--- enabled (Use_MetaLabeling), 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"