derivado de animatedread/Warrior_EA
Signal_CooldownMinutes is a per-chart input like Signal_CooldownBars, and it ran AFTER SignalCooldownOverrideBars - so a chart carrying a stored minutes value would have silently defeated the override, which is precisely the problem the override was added to solve. Latent rather than live: every chart currently holds SCM_OFF. That is the kind of 'works today' that stops working the first time someone sets the input, and it would have looked like the override simply not functioning. Minutes resolve first; the const now has the last word. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
616 linhas
43 KiB
MQL5
616 linhas
43 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` is a GUI-only section divider: MetaTrader renders an input string
|
|
//--- whose value equals its comment as a header. Never read by code.
|
|
//--- NN Optimizer / Performance must stay LAST - AI\Network.mqh's Adam/Sgd inputs render after it.
|
|
//==================================================================================================
|
|
// GENERAL
|
|
//==================================================================================================
|
|
input string Expert_Settings = "General"; // General
|
|
//--- 0 = ASSIGN ONE AND REMEMBER IT. On first attach the EA draws a random magic, writes it to
|
|
//--- MQL5\Files\Warrior_<symbol>_<period>.magic, and reads that same value back on every later start -
|
|
//--- so it is unique without anyone typing it, and STABLE, which is the part that matters: the magic
|
|
//--- is how the EA recognises its own positions. A magic that changed on restart would leave every
|
|
//--- open position invisible to the close-all, the risk-budget flatten and the journal - trades still
|
|
//--- running that no code would ever manage again.
|
|
//--- Set a non-zero value to pin one explicitly instead (back-compat; a chart already carrying 2024
|
|
//--- keeps it, because MT5 stores inputs per chart and an existing attach never sees this default).
|
|
input ulong Expert_MagicNumber = 0; // Magic number (0 = assign + remember automatically)
|
|
input bool Expert_EveryTick = false; // Calculate on every tick
|
|
//--- Also throttles the per-era training journal: false prints each diagnostic on the first eras and
|
|
//--- then every TRAIN_LOG_EVERY_ERAS-th (state CHANGES always print). true is the full firehose.
|
|
input bool VerboseMode = false; // Verbose journal + detailed panel (full per-era logs)
|
|
//--- Dev diagnostics to the Experts journal (plateau stage, deploy gate, selection internals).
|
|
const bool DebuggingMode = false;
|
|
//--- Pins the dense-taper depth instead of deriving it (ComputeHiddenLayerCount). 0 = derived, the only
|
|
//--- value that should ship. Compile-time, so two forced depths cannot run from one .ex5.
|
|
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
|
|
//--- TRADE MANAGEMENT IS THE TESTER GA'S SEARCH SPACE (2026-08-24). The NN's job is the swing label;
|
|
//--- none of these touch a fingerprint or a DB key, so the GA can sweep them without a retrain.
|
|
input TRADING_DIRECTION tradingdirection = BOTH; // Trade direction
|
|
//--- TWO BOOKS, ONE PER SIDE (2026-08-26). ON, and on a RETAIL_HEDGING account, the EA keeps an
|
|
//--- independent long book and short book on this symbol: at most one long and at most one short,
|
|
//--- each opened on its own side's vote and each held to its own barrier. OFF, or on a NETTING
|
|
//--- account, behaviour is exactly as before - one position per symbol, opposite votes ignored.
|
|
//---
|
|
//--- WHY THIS AND NOT A VOTE EXIT. The deploy gate certifies P(label agrees | the vote fired) and
|
|
//--- the label runs to the barrier. Closing early on a reversal vote makes the realised outcome stop
|
|
//--- being the labelled one, so the certified precision no longer describes what is traded. Opening
|
|
//--- the OTHER side instead acts on the new signal while leaving the old position's certification
|
|
//--- intact, and it costs no more than closing-then-reversing would: both pay the new side's spread,
|
|
//--- and the only difference is that the existing position runs on to the barrier its own gate
|
|
//--- already measured as positive-expectancy. That is why Signal_ThresholdClose could be deleted
|
|
//--- rather than tuned - see the ensemble deploy gate.
|
|
//---
|
|
//--- ALLOWED AT THE5ERS - single-account hedging is permitted; what their #12/#17 ban is hedging
|
|
//--- ACROSS accounts or firms (hedge arbitrage, inter-account, cross-firm). Correction recorded
|
|
//--- 2026-08-26 after an earlier reading of #17 wrongly caught this case.
|
|
//---
|
|
//--- NOT in BuildModelFingerprint(): no .nnw is re-keyed by turning this on or off.
|
|
input bool Allow_Hedging = true; // Hedging: independent long + short book (max 1 each)
|
|
input ENTRY_MULTIPLIER Entry_Multiplier = MARKET; // Entry type/offset
|
|
input STOP_LOSS_MODE SL_Mode = SL_ATR_x2; // Stop-loss mode
|
|
input TAKE_PROFIT_MODE TP_Mode = TP_ATR_x6; // Take-profit mode
|
|
input TRAILING_STRATEGY TrailingStrategy = TRAILING_STRATEGY_NONE; // Trailing stop
|
|
input BARS_EXPIRATION Signal_Expiration = BARS_X3; // Pending order expiry (bars)
|
|
//--- Confidence_Source WAS REMOVED 2026-08-25 together with all five confidence-scaled trade-management
|
|
//--- modes it fed (Intelligent entry / SL / TP / trailing / lot size). See CONFIDENCE_SOURCE's removal
|
|
//--- note in Enumerations\InputEnums.mqh. Trade management is now entirely explicit: what the GA sets
|
|
//--- is what the trade gets, which is also what makes a GA sweep of it interpretable.
|
|
//--- CExpertSignal::m_threshold_open / m_threshold_close, on the library's own 0-100 scale: the
|
|
//--- vote is a WEIGHTED MEAN of the firing patterns' weights, which cannot exceed 100.
|
|
//---
|
|
//--- THIS IS A CONSENSUS RULE IN DISGUISE, and its meaning moves with the win rate. The vote is
|
|
//--- sum(moduleWeight x tierWeight x sign) / sum(moduleWeight over every member that EVALUATED the
|
|
//--- bar - abstentions included, by design since 2026-08-19). So with N members of similar weight
|
|
//--- the reachable votes are quantised by how many agree, and the threshold picks the quorum:
|
|
//---
|
|
//--- 4 members, tier weight ~30 (the pivot-event label's win rate):
|
|
//--- 4 agree -> 30 PCT_25 => needs 4 of 4 (UNANIMITY)
|
|
//--- 3 agree, 1 abstain -> 22.5 PCT_20 => needs 3 of 4
|
|
//--- 2 agree, 2 abstain -> 15 PCT_15 => needs 2 of 4
|
|
//---
|
|
//--- PCT_25 was correct while the direction-to-next-pivot label produced ~70% win rates: the
|
|
//--- ceiling was ~70 and 25 asked for about a third of it. Under the pivot-event label the win
|
|
//--- rates are ~30%, so 25 sits at ~83% of the ceiling and has silently become a UNANIMITY rule.
|
|
//--- Measured 2026-08-26: every one of the 6 symbols cleared its precision bar and 4 of 6 failed
|
|
//--- ONLY on coverage, which fell 6.8% -> 2.2% over 35 eras as the four models specialised and
|
|
//--- unanimity got rarer. That also feeds a doom loop - fewer fires shrink effN, which RAISES the
|
|
//--- exact-binomial deploy bar (SP500: 24.1% -> 32.9% at FLAT precision).
|
|
//---
|
|
//--- THIS IS NOW ONLY A SEED (2026-08-26). The era verdict DERIVES the threshold - the highest rung
|
|
//--- whose vote still clears the whole deploy gate - and publishes it to the live signal every tick
|
|
//--- (THE DERIVED THRESHOLD in Expert\AIBase\Training.mqh; CExpertCustom::PublishVoteThreshold).
|
|
//--- The value below is read once, and only governs the bars traded BEFORE the first era has been
|
|
//--- scored. Setting it per chart is no longer necessary and no longer meaningful.
|
|
//---
|
|
//--- WHY IT HAD TO STOP BEING AN INPUT. Two independent reasons, both measured:
|
|
//--- 1. THERE IS NO GOOD GLOBAL VALUE. The right rung differs per symbol AND drifts per era. On
|
|
//--- 619 era verdicts across the six live charts, EVERY era had at least one rung clearing the
|
|
//--- full gate - while at the fixed 25% the fleet was actually running, four of six symbols had
|
|
//--- none, ever. The models were deployable the whole time; the constant was the blocker.
|
|
//--- 2. AN INPUT CANNOT BE CORRECTED. MT5 stores an input's value PER CHART in
|
|
//--- profiles\Charts\*\chart*.chr. The live fleet kept running at 25 across a full
|
|
//--- close/recompile/relaunch cycle with the log still reading "fired at vote>=25%" - changing
|
|
//--- the default here moved nothing. A number that can only be fixed by hand-editing six charts
|
|
//--- is not a parameter, it is a liability.
|
|
//---
|
|
//--- The knob that remains is MIN_COVERAGE_FRACTION_OF_BASE_RATE (ExpertSignalAIBase.mqh) - "how
|
|
//--- much of the market must I catch before I believe the measurement". That is the real policy
|
|
//--- question, and it is one question rather than six per-chart ones.
|
|
//---
|
|
//--- NOT in BuildModelFingerprint(), so none of this re-keys a trained .nnw.
|
|
input PERCENTAGE_PRESETS Signal_ThresholdOpen = PCT_15; // Vote threshold SEED (derived after era 1)
|
|
//--- CLOSE ON THE OPPOSITE VOTE. Signal_ThresholdClose (and its SIGNAL_CLOSE_PRESETS enum) was
|
|
//--- replaced by this boolean 2026-08-26: a second THRESHOLD was always redundant, because "the bot
|
|
//--- now says the other way" is one question, not two. When this is ON the exit fires at the SAME
|
|
//--- derived threshold the entry uses, published to the signal by
|
|
//--- CExpertCustom::PublishVoteThreshold() - so there is still nothing to tune.
|
|
//---
|
|
//--- OFF IS THE DEFAULT, AND THE REASON IS STATISTICAL, NOT A PREFERENCE. The deploy gate certifies
|
|
//--- P(label agrees | the vote fired), and the label runs to the barrier. Close early and the
|
|
//--- realised outcome is no longer the labelled one, so the certified precision stops describing what
|
|
//--- is actually being traded. Turning this on is a DIFFERENT strategy from the one the gate
|
|
//--- measured, and it has not been measured. Treat any win rate on the panel with suspicion until it
|
|
//--- has been.
|
|
//---
|
|
//--- HOW IT COMPOSES WITH Allow_Hedging - they are alternative reversal policies, not independent:
|
|
//--- hedging ON, exit OFF (default) -> the opposite vote OPENS the other book; both positions run
|
|
//--- to their own barriers, both stay certified.
|
|
//--- hedging ON, exit ON -> the long book closes and the short book opens on the same
|
|
//--- pass, so this is classic reverse behaviour and a hedge
|
|
//--- never actually forms.
|
|
//--- hedging OFF, exit ON -> plain close-on-reversal on the single position.
|
|
//--- hedging OFF, exit OFF -> hold to the barrier, opposite votes ignored entirely.
|
|
//---
|
|
//--- It drives CExpertSignalCustom::m_holdToBarrier, which until now NOTHING EVER SET - the whole
|
|
//--- hold-to-barrier mechanism was dormant and the disabled close threshold was doing the work alone.
|
|
input bool Exit_On_Reversal_Vote = false; // Close on opposite vote (default: hold to the barrier)
|
|
//--- OFF = the filtered view, one arrow per position the EA would open (vote + ranking + threshold
|
|
//--- applied). ON = every model's raw opinion, per model - the diagnostic view that shows a collapsed
|
|
//--- member the filtered view cannot, because a collapsed member simply stops appearing in it.
|
|
input bool DrawUnfilteredSignals = false; // Draw raw per-model signals (bypass vote/ranking/threshold)
|
|
//==================================================================================================
|
|
// INDICATOR SEEDS (the AI feature block's starting periods)
|
|
//==================================================================================================
|
|
//--- THE FOUR CLASSIC VOTES WERE REMOVED 2026-08-24 (EnableMA / EnableRSI / EnableMACD /
|
|
//--- EnableIchimoku, and the Classic_Shift input that only they read). All 26 shipped patterns were
|
|
//--- measured as entries on 178k-bar histories across four instruments and three barrier geometries:
|
|
//--- nothing separated from chance individually, by vote threshold, by quorum, or as event plus
|
|
//--- confirmation, and the two bars of lookahead that had once produced a +4 sigma reading were the
|
|
//--- whole of it. All four inputs had shipped false ever since, so this removes dormant code, not
|
|
//--- behaviour. The seeds below stay because the AI FEATURE block still reads them.
|
|
//--- SEEDS ONLY. All indicator parameters are tuner-owned: the auto-tuner searches from these under a
|
|
//--- family-wise gate and persists winners in TunedPeriods_{SYM}_{TF}.cfg, which the AI features read.
|
|
//--- Hand-setting means editing these constants, which deliberately bypasses that 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
|
|
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
|
|
//--- Two or more enabled = an ensemble (|ENS1 fingerprint token + joint vote-level deploy gate);
|
|
//--- exactly one = solo, same fingerprint and files as the old preset; none = classic only. Every
|
|
//--- enabled NN trains a net per chart, so prefer fewer members on sub-daily timeframes.
|
|
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)
|
|
//--- One-shot measurement: an Alglib forest, MLP and OLS fit on the net's OWN windows, labels, split and
|
|
//--- gate arithmetic. Answers whether a flat result is the architecture or the matrix. Nothing trades on
|
|
//--- it and no model is saved. See Expert\Training\BaselineComparator.mqh.
|
|
input bool Run_Alglib_Baselines = true; // Diagnostic: forest + linear on the NN's own matrix
|
|
//--- CROSS-INSTRUMENT TRAINING ROWS. Each chart publishes its own feature rows and adopts its peers',
|
|
//--- so one net trains on several instruments at once while every chart keeps its own model. Measured
|
|
//--- +2.02pp of paired skill at H4 (t_mkt 3.97, clearing its family-wise bar) and replicated at D1;
|
|
//--- the per-instrument arm was NEGATIVE on every feature set. Only peers whose MODEL FINGERPRINT
|
|
//--- matches contribute, so it does nothing until a second chart runs the same configuration.
|
|
//--- Deliberately NOT a fingerprint member: it changes what the model trains on, not what it is.
|
|
input bool Use_Training_Pool = true; // Train on peer charts' rows as well as this chart's
|
|
//--- WHAT THE DIRECTION MODELS LEARN: the swing-pivot direction label, unconditionally. The label
|
|
//--- is geometry-free - it only says which way the next confirmed pivot lies - which is what leaves
|
|
//--- trade management to the tester GA instead of baking it into what the net learns.
|
|
//+------------------------------------------------------------------+
|
|
//| Roster string for logs and the journal's filterID column. Lives |
|
|
//| here because TradeJournalManager.mqh is included before |
|
|
//| Variables.mqh's globals and needs it too. |
|
|
//+------------------------------------------------------------------+
|
|
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";
|
|
return s;
|
|
}
|
|
input ENUM_OPTIMIZATION TrainingOptimizer = ADAM; // Weight optimizer
|
|
//--- The target is 3-way (Buy/Sell/Neutral), so the head is a 3-class softmax. The regression path
|
|
//--- stays implemented but is no longer selectable.
|
|
const OUTPUT_NEURONS_COUNT OutputNeuronsCount = OUTPUT_CLASSIFICATION;
|
|
//--- First-layer width, LSTM hidden size, conv filter count, taper depth and reduction are all
|
|
//--- DERIVED from the post-selection input width and the in-sample bar count - see
|
|
//--- ComputeFirstLayerWidth(), ComputeConvFilterCount(), ComputeLstmHiddenSize().
|
|
const bool EnableBatchNorm = true; // AI: batch normalization
|
|
//--- EMA window for the running mean/variance, in training SAMPLES (there is no mini-batch to average
|
|
//--- over). <=1 silently disables the layer, which is the only other meaningful setting.
|
|
const int BatchNormWindow = 1000; // AI: batch-norm window (samples)
|
|
//--- Training starts at the earliest available bar (floored by MinTrainYear); the honest generalisation
|
|
//--- read comes from this holdout, not from withholding history.
|
|
input OOS_SPLIT_PRESET OOSSplit = OOS_30; // Out-of-sample holdout
|
|
//--- There is no "target accuracy" input: training runs until it stops improving and deploys its
|
|
//--- own best checkpoint (see the PLATEAU_* ladder).
|
|
//==================================================================================================
|
|
// CLASS IMBALANCE - ONE MECHANISM, NO KNOB
|
|
//==================================================================================================
|
|
//--- LOGIT-ADJUSTED LOSS (Menon et al. 2021): add tau*log(prior_c) to each class logit inside the
|
|
//--- TRAINING gradient only, so the raw argmax at inference is already balanced-error-optimal.
|
|
//--- tau is fixed at 1.0 - the full log-prior, the paper's consistent value; the priors come from the
|
|
//--- label prebuild's measured distribution, and the delivered strength is capped to the head's
|
|
//--- usable logit range (see ApplyLogitAdjustment). There is nothing left for a user to choose.
|
|
//--- Freeze the measured class priors after the first measurement. Letting them track is correct since
|
|
//--- the barrier relabel; freezing is a diagnostic for a genuinely shifting distribution.
|
|
const bool FreezePriorCalibration = false;
|
|
//--- Repainting embargo for the swing-context FEATURES (not the labels - their lookahead control is
|
|
//--- the pivot-pair finality rule). ZigZag revises its recent legs, so a raw read would be straight
|
|
//--- lookahead.
|
|
const SWING_CONFIRMATION_PRESET SwingConfirmationBars = SC_100;
|
|
//--- Keep adapting a deployed model on a LIVE chart to newly-RESOLVED bars. The blend FREEZES if a
|
|
//--- rolling-accuracy guardrail decays, so drift cannot reach the account. No effect in the tester.
|
|
const bool EnableOnlineLearning = true;
|
|
//--- SIGNAL COOLDOWN. NOT display-only - the previous comment here said so and was WRONG: when this
|
|
//--- suppresses a bar, CExpertSignalAIBase's live path zeroes the signal outright, so there is no
|
|
//--- arrow, no vote and NO POSITION. It gates trading, the tester and the drawn history alike. What it
|
|
//--- does NOT touch is training: labels, features and backprop never see it, so changing these costs
|
|
//--- no retrain and they are deliberately absent from the fingerprint.
|
|
//---
|
|
//--- INPUTS, not constants, because the right value is per-chart and per-timeframe and could not be
|
|
//--- tuned without a recompile before. The raw per-bar metrics are still never declustered.
|
|
input string CD_Settings = "Signal Cooldown"; // Signal Cooldown
|
|
input SIGNAL_COOLDOWN_SCOPE Signal_CooldownScope = SIGNAL_COOLDOWN_ANY_SIGNAL; // Cooldown: what a signal blocks
|
|
//--- Bars between signals. 0 disables the cooldown entirely.
|
|
//--- DEFAULT 30, and the floor is PRINCIPLED rather than cosmetic: a trade on this label is held for
|
|
//--- 5 + the median ZigZag leg = 18-19 bars, so any second signal inside that window is the SAME
|
|
//--- trade being re-announced. 20 is therefore the smallest defensible value; 30 is one comfortable
|
|
//--- step above it, and was chosen because the measured natural spacing between vote arrows is ~20
|
|
//--- bars - a window at or below that thins almost nothing (10 bars removed only 8-21%).
|
|
input SIGNAL_COOLDOWN_BARS Signal_CooldownBars = SCB_30; // Cooldown: bars between signals
|
|
//--- OVERRIDES the bar count when > 0, so a cooldown can be expressed in wall-clock and stay put
|
|
//--- across a timeframe change. Converted to bars against the CHART's period, so it is exact on
|
|
//--- every timeframe rather than approximated.
|
|
input SIGNAL_COOLDOWN_MINUTES Signal_CooldownMinutes = SCM_OFF; // Cooldown: minutes instead
|
|
//--- ONE resolver, so the member layer and the VOTE layer can never disagree about the window. The
|
|
//--- minutes form overrides the bar count and is resolved against the CHART period, rounded UP so a
|
|
//--- cooldown asked for in wall-clock is never silently shorter than requested.
|
|
//--- SOURCE OVERRIDE, and it exists because of a trap this codebase already documents: MT5 stores an
|
|
//--- input PER CHART in profiles\Charts\*\chart*.chr, so AN ALREADY-ATTACHED EA IGNORES A CHANGED
|
|
//--- DEFAULT ENTIRELY. Raising Signal_CooldownBars from 10 to 30 changed nothing on six live charts -
|
|
//--- they kept reporting a 10-bar window - because each had 10 saved in its own profile.
|
|
//---
|
|
//--- That is precisely why SignalClusterWindow was a const before this work. Making it an input
|
|
//--- restored per-chart tunability and lost source-correctability; this restores the second without
|
|
//--- giving up the first.
|
|
//---
|
|
//--- > 0 WINS over the input. 0 hands control back to the panel. Set it to 0 once the charts have
|
|
//--- been re-attached or their inputs set by hand.
|
|
const int SignalCooldownOverrideBars = 30;
|
|
int WarriorSignalCooldownBars(void)
|
|
{
|
|
int bars = (int)Signal_CooldownBars;
|
|
//--- MINUTES FIRST, so the source override below is genuinely LAST and wins over both. Applying it
|
|
//--- before this block left a latent copy of the very bug it exists to fix: Signal_CooldownMinutes
|
|
//--- is a per-chart input too, so a chart carrying a stored minutes value would have silently
|
|
//--- defeated the override. Harmless while every chart holds SCM_OFF, which is exactly the kind of
|
|
//--- "works today" that stops working the first time someone sets it.
|
|
if(Signal_CooldownMinutes > 0)
|
|
{
|
|
int secs = PeriodSeconds();
|
|
if(secs > 0)
|
|
bars = (int)MathCeil(((int)Signal_CooldownMinutes * 60.0) / secs);
|
|
}
|
|
//--- LAST WORD. Deliberately NOT applied when the resolved value is OFF: an operator who switched
|
|
//--- the cooldown off meant it, and silently re-enabling it from source would be the same class of
|
|
//--- surprise this override exists to fix, pointed the other way.
|
|
if(SignalCooldownOverrideBars > 0 && bars > 0)
|
|
bars = SignalCooldownOverrideBars;
|
|
return (bars > 0) ? bars : 0;
|
|
}
|
|
//--- ONE PASS OVER THE HELD-OUT SLICE, at deploy, after the best checkpoint is restored.
|
|
//---
|
|
//--- WHAT IT BUYS: the OOS slice is the NEWEST history and the model never trains on it, while online
|
|
//--- learning adapts to every bar that resolves AFTER deployment. That leaves a gap exactly at the
|
|
//--- handover, over the most regime-relevant data there is. This is the standard
|
|
//--- select-on-validation-then-refit-on-everything move.
|
|
//---
|
|
//--- WHAT IT COSTS, and it is not nothing: the deployed weights are then NOT the weights that were
|
|
//--- measured. The deploy log's promise - "every model reverts to the weights it held at the era
|
|
//--- whose combined vote scored best, so the ensemble that trades is exactly the one that was
|
|
//--- measured" - stops being literally true. Every certified number belongs to the PRE-PASS weights
|
|
//--- and must be quoted that way.
|
|
//---
|
|
//--- Set false to keep certified == traded exactly.
|
|
const bool EnableOosFinalPass = true;
|
|
//--- A second small net predicting how FAR price travels within the horizon - never which way. Stage 1
|
|
//--- is a MEASUREMENT: it prints a Brier skill score and places no orders. Not in the fingerprint.
|
|
const bool UseExcursionHead = true;
|
|
//--- Runaway backstop, not a training control - the plateau ladder decides when a run ends.
|
|
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
|
|
//--- Bars per input sequence is DERIVED (DeriveHistoryBars) and pinned in the .cfg. The ATR feature
|
|
//--- period is deliberately decoupled and fixed: the indicator is created before the .cfg is adopted,
|
|
//--- so deriving it 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)
|
|
input bool EnableMAFeature = true; // Feature: Moving Average
|
|
//--- Widths are per BAR, so each is multiplied by the sequence length - enable deliberately.
|
|
input bool EnableSwingContext = true; // Feature: ZigZag swing context
|
|
//--- THE RSI, MACD, ICHIMOKU AND FIVE AD/WYCKOFF FEATURE GROUPS WERE REMOVED 2026-08-24. Every one
|
|
//--- had shipped false, and each carried a closed verdict: the classic oscillators are the same 26
|
|
//--- patterns that measured at chance as entries, and the Wyckoff family returned zero out-of-sample
|
|
//--- on five independent instruments, which is what closed the context score. Their widths were
|
|
//--- already 0 in the input matrix, so removing them changes no fingerprint and orphans no model.
|
|
//==================================================================================================
|
|
// AD / WYCKOFF INDICATOR PARAMETERS
|
|
//==================================================================================================
|
|
//--- Tuner seeds, one per CONCEPT rather than per indicator (volClimax/volHigh/rangeClimax/... were
|
|
//--- restated verbatim across four indicators). The auto-tuner is the operator path to these values;
|
|
//--- editing a _DEF is a deliberate speed bump, because hand-set values bypass its family-wise gate.
|
|
#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
|
|
//--- Aliases keeping every consumer (CADIndicatorTuner seeds, ConfigFingerprint's ADP token) untouched.
|
|
#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
|
|
//--- 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
|
|
//--- Currency-strength panel built from the FX pairs in Market Watch. Needs >= 2 usable pairs; degrades
|
|
//--- to a neutral 0-fill with one logged line rather than blocking training.
|
|
input bool EnableCrossAsset = true; // Feature: cross-asset currency strength
|
|
//--- The only microstructure channel that is both FX-available and genuinely historical in the tester.
|
|
//--- Encodes a volatility REGIME; unsigned, like volume, so it can never pick a side.
|
|
input bool EnableSpreadFeature = true; // Feature: spread / volatility regime
|
|
//--- Gates CONSUMPTION only. With 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 shrinks the input width and correctly starts a fresh model.
|
|
input bool EnableAltData = true; // Feature: alternative data (COT / VIX / macro)
|
|
//--- Keys travel as input defaults so wiping Common\Files\Warrior_EA cannot silently kill a source.
|
|
//--- A keys.txt in the AltData folder is consulted only if an input is blanked. COT needs no key.
|
|
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 feature under a family-wise gate; the trial
|
|
//--- budget is derived, not configured (ComputeTuneTrialBudget).
|
|
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 spans 00:00-22:00 GMT. The filter is evaluated once per BAR, so on D1 there is exactly
|
|
//--- one evaluation and a narrow default can starve the EA of entries entirely.
|
|
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
|
|
//--- Its own group because CExpertCustom::OnTick() evaluates this schedule unconditionally - it fires
|
|
//--- whether EnableSessionFilter is on or off. 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
|
|
//--- CH_MARKET_CLOSE resolves per day from the symbol's own session table and backs off by the
|
|
//--- minute setting, so it is right on every symbol and both sides of DST with no number to
|
|
//--- maintain.
|
|
input CLOSE_HOUR_OF_DAY targetHour = CH_MARKET_CLOSE; // Close-all hour
|
|
input CLOSE_MINUTE_OF_HOUR targetMinutes = CM_5; // Close-all minute
|
|
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 RiskGuard_Settings = "Risk Guard"; // Risk Guard
|
|
input bool EnableRiskGuard = true; // Signal: Risk Guard
|
|
//--- Free entry rather than a preset ladder, because prop limits are not always integers. Enter the
|
|
//--- limits from YOUR account agreement, slightly tighter if you want margin for slippage past a stop.
|
|
//--- 0 disables a rule. Enforced at quote frequency by Variables\RiskBudget.mqh, not once per bar.
|
|
input double MaxDailyLossPct = 4.0; // Daily loss limit % (0 = off)
|
|
input double MaxDrawdownPct = 8.0; // Max total drawdown % (0 = off)
|
|
//--- TRUE: measured down from the highest equity ever reached. FALSE: from the equity first seen. Use
|
|
//--- whichever your programme uses - a trailing rule on a static challenge halts far too early.
|
|
input bool MaxDrawdownIsTrailing = true; // Max DD trails the equity peak
|
|
//--- Broker-server hour, NOT local time. A misaligned window hands the allowance back early or late.
|
|
input int RiskDayResetHour = 0; // Risk day reset hour (broker time, 0-23)
|
|
//--- Share of the allowance genuinely LEFT after every open position's remaining loss-to-stop. Without
|
|
//--- it a trade at 3.2% into a 4% day still sized for a full risk unit and a routine stop-out breached.
|
|
input double RiskPerTradeOfBudget = 50.0; // Max % of remaining budget per trade
|
|
//--- Declining new entries cannot stop an ALREADY-OPEN position running through the limit, which is how
|
|
//--- a hard daily rule is actually breached. OFF means the limits above are advisory, not enforced.
|
|
input bool RiskGuardFlatten = false; // Close own positions on breach
|
|
//--- EXPECTANCY STOP. The limits above bound how FAST the account loses, never WHETHER. 0 = off.
|
|
//--- The halt is LATCHED and survives a restart; clearing it means deleting the risk state file.
|
|
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
|
|
//--- Scales each signal's vote by its historical win rate, records every trade, and powers the Export
|
|
//--- Trade Journal Report button.
|
|
input bool UseDatabaseRanking = true; // Weight filters by DB win-rate
|
|
//--- Oldest row pruned past the cap. A high cap costs nothing until the rows exist.
|
|
input int DB_MaxRowsPerTable = 1000000; // Max rows kept per pattern table
|
|
//--- Header only - AI\Network.mqh's Adam*/Sgd* inputs render immediately after this divider.
|
|
input string NNPerf_Settings = "NN Optimizer / Performance"; // NN Optimizer / Performance
|
|
//--- Guarded, so the explicit include in Warrior_EA.mq5 stays harmless: every unit that sees the seed
|
|
//--- constants above also sees the g_Tuned* globals that supersede them.
|
|
#include "TunedPeriods.mqh"
|
|
|
|
//==================================================================================================
|
|
// THE TWO BOOKS - MAGIC NUMBERS
|
|
//==================================================================================================
|
|
//--- Allow_Hedging gives this EA a second position slot on the same symbol, and a position is
|
|
//--- addressed by (symbol, magic) everywhere in MT5 - so the short book needs a magic of its own.
|
|
//--- The LONG book keeps Expert_MagicNumber unchanged: every position, journal row and risk-budget
|
|
//--- state file this EA has ever written stays addressable exactly as before, and turning hedging on
|
|
//--- adopts no existing position into the wrong book.
|
|
//---
|
|
//--- The offset is +1, so magic N and N+1 are BOTH reserved by this EA. Every filter that uses them
|
|
//--- also matches the symbol, so the six charts sharing magic 2024 stay disjoint - but do not point a
|
|
//--- second EA at Expert_MagicNumber+1.
|
|
#define SHORT_BOOK_MAGIC_OFFSET 1
|
|
//--- The vote exit is pinned shut here rather than left at the stock CExpertSignal default of 100:
|
|
//--- the vote is a WEIGHTED MEAN of values that cannot exceed 100, so it CAN reach 100 exactly, and
|
|
//--- 100 would therefore arm an exit this strategy is not certified for. 101 is unreachable by
|
|
//--- arithmetic. See Signal_ThresholdClose's removal note above.
|
|
#define VOTE_EXIT_DISABLED_THRESHOLD 101
|
|
|
|
//--- Band the assigned magics live in. Deliberately far from the values people type by hand (2024,
|
|
//--- 12345, 999) so an auto-assigned magic never collides with a hand-set one on the same account.
|
|
#define WARRIOR_MAGIC_BAND 1400000000
|
|
#define WARRIOR_MAGIC_SLOTS 100000
|
|
//--- Resolved once per run. 0 = not resolved yet; WarriorBookMagic() is called from per-tick filters,
|
|
//--- so the file must be touched exactly once and never again.
|
|
ulong g_warriorBaseMagic = 0;
|
|
|
|
//--- DETERMINISTIC FALLBACK, from chart identity. Used in the tester (where a random magic would make
|
|
//--- two identical passes differ) and whenever the file cannot be written - a magic that is stable
|
|
//--- WITHOUT a file is a far better failure mode than one that is fresh on every start.
|
|
ulong WarriorDerivedMagicBase(void)
|
|
{
|
|
string key = _Symbol + "|" + IntegerToString((int)_Period);
|
|
uint h = 2166136261;
|
|
int n = StringLen(key);
|
|
for(int i = 0; i < n; i++)
|
|
{
|
|
h ^= (uint)StringGetCharacter(key, i);
|
|
h *= 16777619;
|
|
}
|
|
//--- EVEN SLOTS ONLY. The short book is base+1, so odd bases would let one chart's short book land
|
|
//--- exactly on another chart's long book.
|
|
return (WARRIOR_MAGIC_BAND + 2 * (ulong)(h % WARRIOR_MAGIC_SLOTS));
|
|
}
|
|
|
|
string WarriorMagicFileName(void)
|
|
{
|
|
return StringFormat("Warrior_%s_%d.magic", _Symbol, (int)_Period);
|
|
}
|
|
|
|
//--- ASSIGN-ONCE-AND-REMEMBER. Terminal-local (MQL5\Files) ON PURPOSE, not Common: the Common
|
|
//--- Warrior_EA folder is the one that gets wiped for a retrain, and positions outlive retrains. It
|
|
//--- also keeps two terminals running the same symbol on separate magics, which a chart-identity hash
|
|
//--- could not do.
|
|
ulong WarriorResolveBaseMagic(void)
|
|
{
|
|
if(g_warriorBaseMagic != 0)
|
|
return g_warriorBaseMagic;
|
|
//--- Reproducibility beats uniqueness in the tester: two identical passes must not differ.
|
|
if(MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_FORWARD))
|
|
{
|
|
g_warriorBaseMagic = WarriorDerivedMagicBase();
|
|
return g_warriorBaseMagic;
|
|
}
|
|
string file = WarriorMagicFileName();
|
|
//--- SHARE flags on every open, always - see the tester optcache corruption this project already ate.
|
|
int h = FileOpen(file, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE);
|
|
if(h != INVALID_HANDLE)
|
|
{
|
|
string line = FileReadString(h);
|
|
FileClose(h);
|
|
ulong stored = (ulong)StringToInteger(line);
|
|
if(stored != 0)
|
|
{
|
|
g_warriorBaseMagic = stored;
|
|
PrintFormat("Warrior magic: reusing %I64u for %s (from %s) - the positions this EA already"
|
|
" holds stay recognisable across this restart.", stored, _Symbol, file);
|
|
return g_warriorBaseMagic;
|
|
}
|
|
PrintFormat("Warrior magic: %s exists but holds no usable value (\"%s\") - assigning a new one.",
|
|
file, line);
|
|
}
|
|
//--- No usable file. Draw one, then PERSIST IT BEFORE RETURNING - a magic that was never written
|
|
//--- down is exactly the orphaned-positions failure this whole mechanism exists to prevent.
|
|
MathSrand((int)(GetTickCount() + (uint)TimeLocal() + (uint)StringLen(_Symbol) * 7919));
|
|
//--- MathRand() is 0..32767, so one draw cannot cover the slot range. Two do.
|
|
uint draw = ((uint)MathRand() << 15) ^ (uint)MathRand();
|
|
ulong candidate = WARRIOR_MAGIC_BAND + 2 * (ulong)(draw % WARRIOR_MAGIC_SLOTS);
|
|
int w = FileOpen(file, FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE);
|
|
if(w == INVALID_HANDLE)
|
|
{
|
|
g_warriorBaseMagic = WarriorDerivedMagicBase();
|
|
PrintFormat("Warrior magic: CANNOT WRITE %s (error %d) - falling back to the chart-derived magic"
|
|
" %I64u, which is stable without a file. Trading continues; the assigned-random path"
|
|
" is what is unavailable, not position tracking.",
|
|
file, GetLastError(), g_warriorBaseMagic);
|
|
return g_warriorBaseMagic;
|
|
}
|
|
FileWriteString(w, IntegerToString((long)candidate));
|
|
FileClose(w);
|
|
g_warriorBaseMagic = candidate;
|
|
PrintFormat("Warrior magic: assigned %I64u to %s and wrote it to %s. Every later start reads it"
|
|
" back, so positions opened under it stay managed.", candidate, _Symbol, file);
|
|
return g_warriorBaseMagic;
|
|
}
|
|
|
|
ulong WarriorBookMagic(const bool longBook)
|
|
{
|
|
//--- An explicitly pinned value wins and never touches the file: a chart already carrying 2024
|
|
//--- keeps addressing the positions it opened under 2024.
|
|
ulong base = (Expert_MagicNumber != 0) ? Expert_MagicNumber : WarriorResolveBaseMagic();
|
|
return (longBook ? base : base + SHORT_BOOK_MAGIC_OFFSET);
|
|
}
|
|
|
|
//--- IS THIS POSITION/ORDER OURS? Used by every sweep that acts on the EA's own trades - the
|
|
//--- scheduled close-all, the risk budget's emergency flatten, the journal's MAE/MFE walk.
|
|
//---
|
|
//--- DELIBERATELY NOT GATED ON Allow_Hedging. Ownership must not depend on a switch: turn hedging
|
|
//--- off while a short-book position is open and a gated predicate would stop recognising it, so the
|
|
//--- close-all would skip it and the flatten would leave it running - a position no code would ever
|
|
//--- close again. Only OPENING a short book is gated by the input; owning one is forever. This is the
|
|
//--- same asymmetry as ProtectOpenPosition(): an exit may act where an entry may not.
|
|
//--- THE LEGACY PAIR. 2024 was this EA's shipped default magic for its whole life, so positions opened
|
|
//--- under it must stay recognisable even after a chart is switched to the assign-and-remember scheme
|
|
//--- (Expert_MagicNumber = 0). Without this, flipping a chart while a position was open would orphan
|
|
//--- it - still running, invisible to the close-all and the flatten, managed by nothing. Every filter
|
|
//--- that calls this ALSO matches the symbol, so claiming these two values can only ever reach
|
|
//--- positions on this EA's own chart. Safe to delete once no 2024-era position can still be open.
|
|
#define WARRIOR_LEGACY_MAGIC 2024
|
|
|
|
bool WarriorOwnsMagic(const long magic)
|
|
{
|
|
if(magic == (long)WarriorBookMagic(true) || magic == (long)WarriorBookMagic(false))
|
|
return true;
|
|
return (magic == WARRIOR_LEGACY_MAGIC ||
|
|
magic == WARRIOR_LEGACY_MAGIC + SHORT_BOOK_MAGIC_OFFSET);
|
|
}
|
|
|
|
//--- Is the two-book mode actually live? Needs BOTH the input and an account that can hold opposing
|
|
//--- positions - on NETTING the second book is arithmetically impossible, so the EA runs its original
|
|
//--- single-position path and says so once at init.
|
|
bool WarriorHedgingActive(void)
|
|
{
|
|
return (Allow_Hedging &&
|
|
(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
|
|
}
|