 fix(ai): drop the conv pooling stage - it reduced across filters, not time
FeedForwardConv emits POSITION-MAJOR output, matrix_o[out + window_out * i],
so one bar's window_out filter responses are contiguous and consecutive bars
sit window_out apart. Both pooling implementations (FeedForwardProof and
CPU_FeedForwardProof) slide FLAT over that buffer - pos = i * step, reducing
`window` CONSECUTIVE elements. On a position-major layout those neighbours
are different FILTERS of the same bar, never one filter across time.
At the shipped 3/2 the pool computed max(bar0_f0, bar0_f1, bar0_f2), then
max(bar0_f2, bar0_f3, bar0_f4), with every 8th window straddling a bar
boundary. So it collapsed unrelated feature detectors into whichever fired
hardest, passed gradient to that winner only, and halved the feature map
while doing it - all below every learnable layer, where nothing above can
recover it. The removed inputs' own labels ("3 Bars") show time-axis pooling
was the intent throughout.
Measured cost: CONV sat pinned at ~40% balanced accuracy for 510 eras with
Sell recall 0%, while plain MLPs on the same data reached 57-61%. HYBRID,
which also carried this stage, came second-worst of the batch-norm group.
Not fixable in the topology: pooling one filter across time needs a stride
of window_out BETWEEN samples within a window, which a consecutive-window
kernel cannot express at any window/step. That needs a stride-aware kernel
in Network.cl + WarriorCPU.cpp + WarriorDML.cpp and a DLL rebuild, and is
only worth doing if a conv front-end earns its place without downsampling
first - with 20 sliding positions there is little to gain by halving them.
ConvPoolWindow/ConvPoolStep and their enums are removed with it, along with
the |CP: fingerprint term added earlier today.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:28:44 -04:00 | | | //+------------------------------------------------------------------+
|
2026-07-13 03:23:39 -04:00 | | | //| CustomEnums.mqh |
|
| | | //| AnimateDread |
|
| | | //| https://www.mql5.com |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "AnimateDread"
|
| | | #property link "https://www.mql5.com"
|
2026-07-22 17:17:23 -04:00 | | | //--- Weight-update optimizer. This is really an AI\Network.mqh library type; a guarded duplicate is
|
| | | //--- kept here so Variables\Inputs.mqh (which uses it for the TrainingOptimizer input) can be included
|
| | | //--- before the AI headers - putting the EA's own inputs at the top of the Inputs tab. Keep in sync
|
| | | //--- with AI\Network.mqh's copy; the shared WARRIOR_ENUM_OPTIMIZATION_DEFINED guard prevents a
|
| | | //--- duplicate definition whichever header is parsed first.
|
| | | #ifndef WARRIOR_ENUM_OPTIMIZATION_DEFINED
|
| | | #define WARRIOR_ENUM_OPTIMIZATION_DEFINED
|
2026-07-29 00:03:54 -04:00 | | | //--- A third DFA entry was removed 2026-07-28 - see AI\Network.mqh's copy for the full rationale (it was
|
| | | //--- a deterministic index-parity sign flip on the gradient, i.e. ascent on half of every weight tensor,
|
| | | //--- not Direct Feedback Alignment). SGD/ADAM keep ordinals 0/1: they feed the weights-filename
|
| | | //--- fingerprint and must never be renumbered.
|
2026-07-22 17:17:23 -04:00 | | | enum ENUM_OPTIMIZATION
|
| | | {
|
| | | SGD, // SGD + Momentum (heavy-ball, simpler, needs more eras)
|
2026-07-29 00:03:54 -04:00 | | | ADAM // Adam (adaptive step, faster convergence, can overfit)
|
2026-07-22 17:17:23 -04:00 | | | };
|
| | | #endif
|
2026-07-22 22:51:04 -04:00 | | | //--- Logical, commonly-used Moving Average / RSI periods only - keeps the Classic Signals inputs (and
|
| | | //--- the AutoTuneIndicators search space over them, see ADIndicatorTuner.mqh) from being set/perturbed
|
| | | //--- to an arbitrary, non-standard period.
|
| | | enum MA_PERIOD_PRESETS
|
| | | {
|
| | | MA_PERIOD_5 = 5, // 5
|
| | | MA_PERIOD_8 = 8, // 8
|
| | | MA_PERIOD_9 = 9, // 9
|
| | | MA_PERIOD_10 = 10, // 10
|
| | | MA_PERIOD_13 = 13, // 13
|
| | | MA_PERIOD_20 = 20, // 20
|
| | | MA_PERIOD_21 = 21, // 21
|
| | | MA_PERIOD_50 = 50, // 50
|
| | | MA_PERIOD_100 = 100, // 100
|
| | | MA_PERIOD_200 = 200, // 200
|
| | | };
|
 feat(indicators): run the built-in iMA and MetaTrader's ZigZag; add a classic-vote shift
MA: CustomIndicators\ADMovingAverage is replaced by the built-in iMA (CiMA) on
both consumers - the classic vote and the NN MA input feature. This drops the
five advanced types ALMA/DEMA/ZLEMA/T3/Kalman, which have no iMA equivalent;
MA_TYPE_PRESETS is now ENUM_MA_METHOD's own codes and the tuner searches all
four. It also removes a documented failure mode: a custom indicator's depth is
bounded by TERMINAL_MAXBARS, and m_MA was the one whose feature block REJECTS
the bar on a short read - the "feature 25 fails on every bar" incident of
2026-08-17. A built-in is served at any depth.
MIGRATION. SMA moves from code 5 to 0, so persisted type codes change meaning.
SanitizeMaType() is the single validity rule; TunedPeriods records now carry a
version field and a v1 record remaps 5..8 -> 0..3, falling back to SMA for a
stored advanced type (unrecoverable - old 0..4 are indistinguishable from valid
new codes). Existing .nnw files re-key on their own, because MA_Type is hashed
into the topology fingerprint, so models retrain rather than silently running
on different MA values. EXPECT A FULL RETRAIN.
ZigZag: ADZigZag was a byte-identical rename of MetaQuotes' Examples\ZigZag -
verified by normalising identifiers and stripping comments, 233 significant
lines each with only renamed symbols differing. It now loads the stock one, so
nothing is bundled and MetaQuotes' fixes arrive without a rebuild here. Both
#resource entries are gone.
Classic_Shift: a new input, the BAR the four classic votes evaluate on (0 =
forming, 1 = last closed, default 1). One implementation on CExpertSignalCustom,
inherited by all four rather than repeated per module. Defaults to a sentinel
meaning "unset", so the AI signals and the aggregate keep the stock every_tick
rule and their feature/label alignment is untouched. The META corpus sweep still
takes precedence. CExpertBase::StartIndex turns out to be virtual, so this is a
real override, not the name-hiding the old comment claimed.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:09:58 -04:00 | | | //--- Moving-average TYPE. VALUES ARE ENUM_MA_METHOD's own codes and MUST stay in sync with it - both the
|
| | | //--- classic MA vote (Signals\SignalMA.mqh) and the NN MA input feature now run the BUILT-IN iMA via
|
| | | //--- CiMA, so a value here is passed straight through as the ma_method argument. Auto-tuner-searchable.
|
| | | //---
|
| | | //--- 2026-08-19: replaced CustomIndicators\ADMovingAverage. That indicator offered five extra types
|
| | | //--- (ALMA/DEMA/ZLEMA/T3/Kalman) on codes 0..4 with SMA/EMA/SMMA/LWMA on 5..8; those five have no iMA
|
| | | //--- equivalent and are GONE, and the four survivors renumbered to match ENUM_MA_METHOD. Anything that
|
| | | //--- persists a type code across that boundary must migrate - see LoadTunedPeriods().
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00 | | | enum MA_TYPE_PRESETS
|
| | | {
|
 feat(indicators): run the built-in iMA and MetaTrader's ZigZag; add a classic-vote shift
MA: CustomIndicators\ADMovingAverage is replaced by the built-in iMA (CiMA) on
both consumers - the classic vote and the NN MA input feature. This drops the
five advanced types ALMA/DEMA/ZLEMA/T3/Kalman, which have no iMA equivalent;
MA_TYPE_PRESETS is now ENUM_MA_METHOD's own codes and the tuner searches all
four. It also removes a documented failure mode: a custom indicator's depth is
bounded by TERMINAL_MAXBARS, and m_MA was the one whose feature block REJECTS
the bar on a short read - the "feature 25 fails on every bar" incident of
2026-08-17. A built-in is served at any depth.
MIGRATION. SMA moves from code 5 to 0, so persisted type codes change meaning.
SanitizeMaType() is the single validity rule; TunedPeriods records now carry a
version field and a v1 record remaps 5..8 -> 0..3, falling back to SMA for a
stored advanced type (unrecoverable - old 0..4 are indistinguishable from valid
new codes). Existing .nnw files re-key on their own, because MA_Type is hashed
into the topology fingerprint, so models retrain rather than silently running
on different MA values. EXPECT A FULL RETRAIN.
ZigZag: ADZigZag was a byte-identical rename of MetaQuotes' Examples\ZigZag -
verified by normalising identifiers and stripping comments, 233 significant
lines each with only renamed symbols differing. It now loads the stock one, so
nothing is bundled and MetaQuotes' fixes arrive without a rebuild here. Both
#resource entries are gone.
Classic_Shift: a new input, the BAR the four classic votes evaluate on (0 =
forming, 1 = last closed, default 1). One implementation on CExpertSignalCustom,
inherited by all four rather than repeated per module. Defaults to a sentinel
meaning "unset", so the AI signals and the aggregate keep the stock every_tick
rule and their feature/label alignment is untouched. The META corpus sweep still
takes precedence. CExpertBase::StartIndex turns out to be virtual, so this is a
real override, not the name-hiding the old comment claimed.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:09:58 -04:00 | | | MA_TYPE_SMA = MODE_SMA, // SMA (simple)
|
| | | MA_TYPE_EMA = MODE_EMA, // EMA (exponential)
|
| | | MA_TYPE_SMMA = MODE_SMMA, // SMMA (smoothed)
|
| | | MA_TYPE_LWMA = MODE_LWMA, // LWMA (linear weighted)
|
feat: add unified MA type support to indicator tuner
Add `MA_TYPE_PRESETS` enum covering advanced (ALMA, DEMA, ZLEMA, T3, Kalman) and standard (SMA, EMA, SMMA, LWMA) moving averages. Integrate `maType` and `bestMaType` into `CADIndicatorTuner` struct, update flatten/unflatten routines, and bump `AD_TUNE_PARAM_COUNT` to 33. This allows the auto-tuner to search over MA type alongside period, improving feature discovery.
2026-07-23 15:02:09 -04:00 | | | };
|
 feat(indicators): run the built-in iMA and MetaTrader's ZigZag; add a classic-vote shift
MA: CustomIndicators\ADMovingAverage is replaced by the built-in iMA (CiMA) on
both consumers - the classic vote and the NN MA input feature. This drops the
five advanced types ALMA/DEMA/ZLEMA/T3/Kalman, which have no iMA equivalent;
MA_TYPE_PRESETS is now ENUM_MA_METHOD's own codes and the tuner searches all
four. It also removes a documented failure mode: a custom indicator's depth is
bounded by TERMINAL_MAXBARS, and m_MA was the one whose feature block REJECTS
the bar on a short read - the "feature 25 fails on every bar" incident of
2026-08-17. A built-in is served at any depth.
MIGRATION. SMA moves from code 5 to 0, so persisted type codes change meaning.
SanitizeMaType() is the single validity rule; TunedPeriods records now carry a
version field and a v1 record remaps 5..8 -> 0..3, falling back to SMA for a
stored advanced type (unrecoverable - old 0..4 are indistinguishable from valid
new codes). Existing .nnw files re-key on their own, because MA_Type is hashed
into the topology fingerprint, so models retrain rather than silently running
on different MA values. EXPECT A FULL RETRAIN.
ZigZag: ADZigZag was a byte-identical rename of MetaQuotes' Examples\ZigZag -
verified by normalising identifiers and stripping comments, 233 significant
lines each with only renamed symbols differing. It now loads the stock one, so
nothing is bundled and MetaQuotes' fixes arrive without a rebuild here. Both
#resource entries are gone.
Classic_Shift: a new input, the BAR the four classic votes evaluate on (0 =
forming, 1 = last closed, default 1). One implementation on CExpertSignalCustom,
inherited by all four rather than repeated per module. Defaults to a sentinel
meaning "unset", so the AI signals and the aggregate keep the stock every_tick
rule and their feature/label alignment is untouched. The META corpus sweep still
takes precedence. CExpertBase::StartIndex turns out to be virtual, so this is a
real override, not the name-hiding the old comment claimed.
Not compiled - MetaEditor compile pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:09:58 -04:00 | | | //--- The ONE validity rule for a persisted MA type code. iMA rejects anything outside ENUM_MA_METHOD,
|
| | | //--- and a stored code can predate the ADMovingAverage removal, so every load path runs it through
|
| | | //--- here. Old codes 5..8 were SMA/EMA/SMMA/LWMA and map cleanly; old 0..4 were the five advanced
|
| | | //--- types that no longer exist and are indistinguishable from valid new codes, so they cannot be
|
| | | //--- rescued - callers that know they are reading a pre-migration file pass legacy=true to convert.
|
| | | int SanitizeMaType(const int stored, const bool legacy)
|
| | | {
|
| | | if(legacy)
|
| | | return (stored >= 5 && stored <= 8) ? stored - 5 : (int)MA_TYPE_SMA;
|
| | | return (stored >= MODE_SMA && stored <= MODE_LWMA) ? stored : (int)MA_TYPE_SMA;
|
| | | }
|
2026-07-22 22:51:04 -04:00 | | | enum RSI_PERIOD_PRESETS
|
| | | {
|
| | | RSI_PERIOD_2 = 2, // 2
|
| | | RSI_PERIOD_5 = 5, // 5
|
| | | RSI_PERIOD_7 = 7, // 7
|
| | | RSI_PERIOD_9 = 9, // 9
|
2026-07-26 18:48:34 -04:00 | | | RSI_PERIOD_14 = 14, // 14 (classic)
|
2026-07-22 22:51:04 -04:00 | | | RSI_PERIOD_21 = 21, // 21
|
| | | RSI_PERIOD_25 = 25, // 25
|
| | | };
|
2026-07-26 18:33:12 -04:00 | | | //--- MACD periods (Signals\SignalMACD.mqh classic vote + the MACD input feature). The preset SETS are
|
| | | //--- deliberately chosen so that EVERY fast/slow combination satisfies CSignalMACD::ValidationSettings()'s
|
| | | //--- "slow must exceed fast" rule - the fast list tops out at 15, the slow list starts at 17. A trader
|
| | | //--- picking two legal-looking values from the dropdowns can therefore never produce a combination that
|
| | | //--- fails init, and the auto-tuner (ADIndicatorTuner::PerturbRandom) can perturb either one in isolation
|
| | | //--- without having to know the other's current value.
|
| | | enum MACD_FAST_PRESETS
|
| | | {
|
| | | MACD_FAST_5 = 5, // 5
|
| | | MACD_FAST_8 = 8, // 8
|
| | | MACD_FAST_12 = 12, // 12 (classic)
|
| | | MACD_FAST_15 = 15, // 15
|
| | | };
|
| | | enum MACD_SLOW_PRESETS
|
| | | {
|
| | | MACD_SLOW_17 = 17, // 17
|
| | | MACD_SLOW_21 = 21, // 21
|
| | | MACD_SLOW_26 = 26, // 26 (classic)
|
| | | MACD_SLOW_34 = 34, // 34
|
| | | MACD_SLOW_50 = 50, // 50
|
| | | };
|
| | | enum MACD_SIGNAL_PRESETS
|
| | | {
|
| | | MACD_SIGNAL_5 = 5, // 5
|
| | | MACD_SIGNAL_7 = 7, // 7
|
| | | MACD_SIGNAL_9 = 9, // 9 (classic)
|
| | | MACD_SIGNAL_12 = 12, // 12
|
| | | };
|
| | | //--- Ichimoku periods (Signals\SignalIchimoku.mqh classic vote + the Ichimoku input feature). Same
|
| | | //--- all-combinations-are-legal design as the MACD presets above, against
|
| | | //--- CSignalIchimoku::ValidationSettings()'s "Tenkan < Kijun < Senkou B" rule: Tenkan tops out at 20,
|
| | | //--- Kijun spans 22-40, Senkou B starts at 44. The classic 9/26/52 triple is in the middle of each.
|
| | | enum ICHIMOKU_TENKAN_PRESETS
|
| | | {
|
| | | ICHI_TENKAN_7 = 7, // 7
|
| | | ICHI_TENKAN_9 = 9, // 9 (classic)
|
| | | ICHI_TENKAN_12 = 12, // 12
|
| | | ICHI_TENKAN_20 = 20, // 20
|
| | | };
|
| | | enum ICHIMOKU_KIJUN_PRESETS
|
| | | {
|
| | | ICHI_KIJUN_22 = 22, // 22
|
| | | ICHI_KIJUN_26 = 26, // 26 (classic)
|
| | | ICHI_KIJUN_30 = 30, // 30
|
| | | ICHI_KIJUN_40 = 40, // 40
|
| | | };
|
| | | enum ICHIMOKU_SENKOU_PRESETS
|
| | | {
|
| | | ICHI_SENKOU_44 = 44, // 44
|
| | | ICHI_SENKOU_52 = 52, // 52 (classic)
|
| | | ICHI_SENKOU_60 = 60, // 60
|
| | | ICHI_SENKOU_120 = 120, // 120
|
| | | };
|
2026-07-13 03:23:39 -04:00 | | | //--- custom enumerations for certain settings, minimizes overfitting
|
| | | enum IND_PERIODS_PRESETS
|
| | | {
|
| | | PERIOD_5 = 5, // 5 Periods
|
| | | PERIOD_10 = 10, // 10 Periods
|
2026-07-26 18:48:34 -04:00 | | | PERIOD_14 = 14, // 14 Periods (classic)
|
2026-07-13 03:23:39 -04:00 | | | PERIOD_20 = 20, // 20 Periods
|
| | | PERIOD_30 = 30, // 30 Periods
|
| | | PERIOD_50 = 50, // 50 Periods
|
| | | PERIOD_100 = 100, // 100 Periods
|
| | | PERIOD_200 = 200, // 200 Periods
|
| | | };
|
| | | enum TRAINING_YEARS_PRESET
|
| | | {
|
| | | YEARS_1 = 1, // 1 year
|
| | | YEARS_2 = 2, // 2 years
|
| | | YEARS_5 = 5, // 5 years
|
| | | YEARS_10 = 10, // 10 years
|
| | | YEARS_20 = 20, // 20 years
|
| | | };
|
 feat(trade): anchor SL and TP to the entry price, not the last swing
Stops keyed to the recent swing extreme make a trade's risk a function of
how far the last swing happens to sit rather than of current volatility. On
a shallow pullback the swing sits close to the fill, so the stop is tight
enough to be taken out by noise on setups that then run to target - which is
what the Perceptron's signals were showing.
SL: lowest_low/highest_high -/+ mult*ATR -> entry -/+ mult*ATR
TP: TP_PREV_SWING (opposite swing) -> removed; ATR-from-entry
SL_PREV_SWING, TP_PREV_SWING -> removed from the enums
The SL anchors to `price` (the resolved entry), not to base_price: with a
pending entry those differ by the whole entry offset, and the risk Money
sizes against is entry-to-stop.
MIN_SL_ATR_MULTIPLIER 2.0 -> 0.5. That floor existed because a swing-
anchored stop could land arbitrarily close to the entry and needed a bound
unrelated to the chosen multiple. An entry-anchored stop is exactly
mult*ATR by construction and cannot collapse, so leaving it at 2.0 would
have silently overridden SL_ATR_x1 to 2*ATR - making the input a lie AND
forcing TP >= 4*ATR just to clear the default 1:2 rejection filter. The
broker's own stop level is enforced separately and precisely by
TCAdjustStops(), so this is now a pure sanity net.
Default TP_Mode TP_PREV_SWING -> TP_ATR_x3, so SL_ATR_x1 + TP_ATR_x3 gives a
realised 3:1 against the 1:2 filter. TP_ATR_x2 would sit EXACTLY on the 2.0
boundary where price-normalization rounding alone can reject the setup; the
default leaves a deliberate gap. This is the same interaction that once
rejected 100% of setups on every symbol (see TP_INTELLIGENT_BASE_RR).
Swing validity guards now reject only when the configuration actually uses a
swing - i.e. ENTRY_PREV_SWING. Previously an unsynced or thin history
rejected EVERY trade, including configurations whose levels no longer
reference a swing at all. The guards are kept, not deleted: a bad swing must
still never reach an entry price, and iLow/iHigh are no longer called with a
possibly-negative index.
TP_INTELLIGENT stays risk-relative. Now that risk is exactly mult*ATR the
risk- and ATR-relative forms coincide, but risk-relative keeps its
reward:risk guarantee exact after the floor or TCAdjustStops widens a stop.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:11:13 -04:00 | | | //--- Stop-loss sizing mode. The ATR_* presets place the SL a fixed multiple of ATR FROM THE ENTRY
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- PRICE.
|
| | | //--- SL_INTELLIGENT (-1) WAS REMOVED 2026-08-25 with the rest of the confidence-scaled trade
|
| | | //--- management: it multiplied the base ATR distance by (1 - 0.3 * confidence), i.e. it staked real
|
| | | //--- risk on a number the project has measured as MISCALIBRATED (the model over-calls by roughly 10x
|
| | | //--- against the label prior - see the calibration verdict). Nothing ever demonstrated that a
|
| | | //--- high-confidence bar deserves a tighter stop; the confidence-vs-outcome buckets in
|
| | | //--- Database\TradeJournalReport.mqh are still recorded, so the claim remains testable, but it does
|
| | | //--- not get to move a stop until it is.
|
 feat(trade): anchor SL and TP to the entry price, not the last swing
Stops keyed to the recent swing extreme make a trade's risk a function of
how far the last swing happens to sit rather than of current volatility. On
a shallow pullback the swing sits close to the fill, so the stop is tight
enough to be taken out by noise on setups that then run to target - which is
what the Perceptron's signals were showing.
SL: lowest_low/highest_high -/+ mult*ATR -> entry -/+ mult*ATR
TP: TP_PREV_SWING (opposite swing) -> removed; ATR-from-entry
SL_PREV_SWING, TP_PREV_SWING -> removed from the enums
The SL anchors to `price` (the resolved entry), not to base_price: with a
pending entry those differ by the whole entry offset, and the risk Money
sizes against is entry-to-stop.
MIN_SL_ATR_MULTIPLIER 2.0 -> 0.5. That floor existed because a swing-
anchored stop could land arbitrarily close to the entry and needed a bound
unrelated to the chosen multiple. An entry-anchored stop is exactly
mult*ATR by construction and cannot collapse, so leaving it at 2.0 would
have silently overridden SL_ATR_x1 to 2*ATR - making the input a lie AND
forcing TP >= 4*ATR just to clear the default 1:2 rejection filter. The
broker's own stop level is enforced separately and precisely by
TCAdjustStops(), so this is now a pure sanity net.
Default TP_Mode TP_PREV_SWING -> TP_ATR_x3, so SL_ATR_x1 + TP_ATR_x3 gives a
realised 3:1 against the 1:2 filter. TP_ATR_x2 would sit EXACTLY on the 2.0
boundary where price-normalization rounding alone can reject the setup; the
default leaves a deliberate gap. This is the same interaction that once
rejected 100% of setups on every symbol (see TP_INTELLIGENT_BASE_RR).
Swing validity guards now reject only when the configuration actually uses a
swing - i.e. ENTRY_PREV_SWING. Previously an unsynced or thin history
rejected EVERY trade, including configurations whose levels no longer
reference a swing at all. The guards are kept, not deleted: a bad swing must
still never reach an entry price, and iLow/iHigh are no longer called with a
possibly-negative index.
TP_INTELLIGENT stays risk-relative. Now that risk is exactly mult*ATR the
risk- and ATR-relative forms coincide, but risk-relative keeps its
reward:risk guarantee exact after the floor or TCAdjustStops widens a stop.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:11:13 -04:00 | | | //--- SWING-ANCHORED STOPS WERE REMOVED 2026-07-31. Both the ATR presets ("N ATR beyond the swing") and
|
| | | //--- SL_PREV_SWING ("exactly at the swing") keyed the stop to the recent swing high/low, which makes
|
| | | //--- the risk on a trade a function of how far away the last swing happens to sit rather than of
|
| | | //--- current volatility: a shallow pullback produced a stop tight enough to be taken out by noise on a
|
| | | //--- setup that then ran to target. Anchoring to the entry makes risk exactly N*ATR by construction,
|
2026-08-09 14:51:59 -04:00 | | | //--- which is also what kept the old minimum-reward:risk rejection satisfiable without depending on
|
| | | //--- swing geometry. That filter is gone (2026-08-09); the coupling is still the right shape.
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- SL_Mode and TP_Mode NO LONGER DEFINE THE TRAINING LABELS. That was true from the 2026-08-01
|
| | | //--- triple-barrier relabel until the swing-pivot target replaced it: the label is now geometry-free
|
| | | //--- (which way the next confirmed pivot lies), neither mode appears in BuildModelFingerprint() or
|
| | | //--- ComputeDbConfigFingerprint(), and both are free for the tester GA to sweep without a retrain.
|
2026-07-22 13:33:56 -04:00 | | | enum STOP_LOSS_MODE
|
| | | {
|
 perf(autotune): replace the genetic search with a filter score - hours to seconds
MEASURED COST OF THE GA, which is what retired it. Per generation:
rung 0: 8 cand x 3 seeds x 3 eras = 72 eras
rung 1: 4 cand x 3 seeds x 8 eras = 96
rung 2: 2 cand x 3 seeds x 20 eras = 120
= 288 eras/generation x 4 generations = 1152 eras BEFORE the winner's
real training began. Against the observed era times on SP500 H1:
PAI 29.1 s/era -> 9.3 h (matches the observed 00:37 -> 09:22)
CONV 41.3 s/era -> 13.2 h
LSTM 150.4 s/era -> 48.1 h
HYBRID 154.6 s/era -> 49.5 h
Two days to tune is not a first-run experience, and it is the phase in
which the panel goes quiet, which is what made it look like a hang.
It also bought nothing. The space is 90 points (10 MA periods x 9 MA
types), so 1152 evaluations revisited each point ~13 times; and rungs of
3 and 8 eras cannot separate two MA periods at all. The 2026-08-01 run
proves it: every finalist scored 25.0-25.9% balanced accuracy - below the
33.3% one-class floor, i.e. indistinguishable noise - and the search then
"deployed the winner" of that.
THE ERROR WAS THE SCORING FUNCTION, not its constants. Using a full
training run to choose a feature's period is a wrapper method paying
wrapper prices for a decision that does not need one. The reference book
does not do this: ch. 3.3 selects inputs by measuring each candidate
indicator's CORRELATION with the target and dropping the ones with none,
with no network involved.
So: rank candidates by the MUTUAL INFORMATION between the resulting
feature vector and the triple-barrier label. MI rather than correlation
because the label is 3-class categorical and the features are not
monotonically related to it. Equal-FREQUENCY binning (rank-based),
because these features are ATR-normalised and heavy-tailed - fixed-width
bins put nearly everything in one bucket and report ~0 information for a
genuinely useful feature.
Scoring is arithmetic over the feature cache, so it costs seconds and its
cost is independent of topology: LSTM now tunes as fast as the MLP.
Coordinate sweep, not product sweep - cost is the SUM of per-parameter
candidate counts, so enabling every indicator stays affordable - with a
second pass that breaks early once nothing moves.
Sampling is IS-ONLY. Letting the OOS window influence which indicator
settings ship would mean the holdout had been used for selection and had
stopped being a holdout.
HONEST LIMIT, recorded because it is the price: MI is marginal, so a
parameter that only pays off in combination with another can be missed
(Guyon & Elisseeff 2003, filter vs wrapper). Given the wrapper it
replaces was ranking pure noise at 48 h a run, this is strictly better.
Deleted with it: GaRungEras/GaExtract/GaStore/GaMutate/GaRandomCandidate/
GaBlockCrossover/GaSortAliveByScoreDesc/GaBreedNextGeneration, 14 m_ga*
members, the GA_*/TUNE_POP_* constants, and ComputeTuneTrialBudget.
AND m_evalMode/m_evalEraBudget, because nothing set them any more - 28
read sites all permanently inert. That is not a tidy-up: the `if
(!m_evalMode)` guard on UpdateClassPriors is exactly what silently
disabled the imbalance correction for entire runs two commits ago. Dead
machinery that still reads like live machinery is this codebase's most
expensive recurring bug, and leaving 28 more instances of it would have
been indefensible.
The panel's tuning-progress state goes too - tuning no longer takes long
enough to need one.
Both builds compile 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:29:31 -04:00 | | | SL_ATR_x1 = 1, // ATR * 1 from entry
|
 feat(trade): anchor SL and TP to the entry price, not the last swing
Stops keyed to the recent swing extreme make a trade's risk a function of
how far the last swing happens to sit rather than of current volatility. On
a shallow pullback the swing sits close to the fill, so the stop is tight
enough to be taken out by noise on setups that then run to target - which is
what the Perceptron's signals were showing.
SL: lowest_low/highest_high -/+ mult*ATR -> entry -/+ mult*ATR
TP: TP_PREV_SWING (opposite swing) -> removed; ATR-from-entry
SL_PREV_SWING, TP_PREV_SWING -> removed from the enums
The SL anchors to `price` (the resolved entry), not to base_price: with a
pending entry those differ by the whole entry offset, and the risk Money
sizes against is entry-to-stop.
MIN_SL_ATR_MULTIPLIER 2.0 -> 0.5. That floor existed because a swing-
anchored stop could land arbitrarily close to the entry and needed a bound
unrelated to the chosen multiple. An entry-anchored stop is exactly
mult*ATR by construction and cannot collapse, so leaving it at 2.0 would
have silently overridden SL_ATR_x1 to 2*ATR - making the input a lie AND
forcing TP >= 4*ATR just to clear the default 1:2 rejection filter. The
broker's own stop level is enforced separately and precisely by
TCAdjustStops(), so this is now a pure sanity net.
Default TP_Mode TP_PREV_SWING -> TP_ATR_x3, so SL_ATR_x1 + TP_ATR_x3 gives a
realised 3:1 against the 1:2 filter. TP_ATR_x2 would sit EXACTLY on the 2.0
boundary where price-normalization rounding alone can reject the setup; the
default leaves a deliberate gap. This is the same interaction that once
rejected 100% of setups on every symbol (see TP_INTELLIGENT_BASE_RR).
Swing validity guards now reject only when the configuration actually uses a
swing - i.e. ENTRY_PREV_SWING. Previously an unsynced or thin history
rejected EVERY trade, including configurations whose levels no longer
reference a swing at all. The guards are kept, not deleted: a bad swing must
still never reach an entry price, and iLow/iHigh are no longer called with a
possibly-negative index.
TP_INTELLIGENT stays risk-relative. Now that risk is exactly mult*ATR the
risk- and ATR-relative forms coincide, but risk-relative keeps its
reward:risk guarantee exact after the floor or TCAdjustStops widens a stop.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:11:13 -04:00 | | | SL_ATR_x2 = 2, // ATR * 2 from entry
|
| | | SL_ATR_x3 = 3, // ATR * 3 from entry
|
2026-07-22 13:33:56 -04:00 | | | };
|
| | | //--- Take-profit sizing mode. The ATR_* presets set the TP a fixed multiple of ATR FROM THE ENTRY
|
2026-08-09 14:51:59 -04:00 | | | //--- PRICE (no longer derived from the reward:risk ratio - that ratio was a pure
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- rejection filter).
|
| | | //--- TP_INTELLIGENT (-1) WAS REMOVED 2026-08-25 for the same reason as SL_INTELLIGENT above - it
|
| | | //--- widened the target to 2.5R * (1 + confidence), so an over-confident model quietly set itself a
|
| | | //--- target it then had to reach.
|
 feat(trade): anchor SL and TP to the entry price, not the last swing
Stops keyed to the recent swing extreme make a trade's risk a function of
how far the last swing happens to sit rather than of current volatility. On
a shallow pullback the swing sits close to the fill, so the stop is tight
enough to be taken out by noise on setups that then run to target - which is
what the Perceptron's signals were showing.
SL: lowest_low/highest_high -/+ mult*ATR -> entry -/+ mult*ATR
TP: TP_PREV_SWING (opposite swing) -> removed; ATR-from-entry
SL_PREV_SWING, TP_PREV_SWING -> removed from the enums
The SL anchors to `price` (the resolved entry), not to base_price: with a
pending entry those differ by the whole entry offset, and the risk Money
sizes against is entry-to-stop.
MIN_SL_ATR_MULTIPLIER 2.0 -> 0.5. That floor existed because a swing-
anchored stop could land arbitrarily close to the entry and needed a bound
unrelated to the chosen multiple. An entry-anchored stop is exactly
mult*ATR by construction and cannot collapse, so leaving it at 2.0 would
have silently overridden SL_ATR_x1 to 2*ATR - making the input a lie AND
forcing TP >= 4*ATR just to clear the default 1:2 rejection filter. The
broker's own stop level is enforced separately and precisely by
TCAdjustStops(), so this is now a pure sanity net.
Default TP_Mode TP_PREV_SWING -> TP_ATR_x3, so SL_ATR_x1 + TP_ATR_x3 gives a
realised 3:1 against the 1:2 filter. TP_ATR_x2 would sit EXACTLY on the 2.0
boundary where price-normalization rounding alone can reject the setup; the
default leaves a deliberate gap. This is the same interaction that once
rejected 100% of setups on every symbol (see TP_INTELLIGENT_BASE_RR).
Swing validity guards now reject only when the configuration actually uses a
swing - i.e. ENTRY_PREV_SWING. Previously an unsynced or thin history
rejected EVERY trade, including configurations whose levels no longer
reference a swing at all. The guards are kept, not deleted: a bad swing must
still never reach an entry price, and iLow/iHigh are no longer called with a
possibly-negative index.
TP_INTELLIGENT stays risk-relative. Now that risk is exactly mult*ATR the
risk- and ATR-relative forms coincide, but risk-relative keeps its
reward:risk guarantee exact after the floor or TCAdjustStops widens a stop.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:11:13 -04:00 | | | //--- TP_PREV_SWING REMOVED 2026-07-31 alongside the swing-anchored stops: targeting the opposite swing
|
| | | //--- caps the reward at whatever structure happens to be overhead, which on a trending signal exits
|
| | | //--- well before the move is done and, paired with a swing-anchored stop, made the realised
|
| | | //--- reward:risk a property of the chart's geometry rather than of the setup.
|
2026-07-22 13:33:56 -04:00 | | | enum TAKE_PROFIT_MODE
|
| | | {
|
| | | TP_ATR_x1 = 1, // ATR * 1 from entry
|
| | | TP_ATR_x2 = 2, // ATR * 2 from entry
|
 perf(autotune): replace the genetic search with a filter score - hours to seconds
MEASURED COST OF THE GA, which is what retired it. Per generation:
rung 0: 8 cand x 3 seeds x 3 eras = 72 eras
rung 1: 4 cand x 3 seeds x 8 eras = 96
rung 2: 2 cand x 3 seeds x 20 eras = 120
= 288 eras/generation x 4 generations = 1152 eras BEFORE the winner's
real training began. Against the observed era times on SP500 H1:
PAI 29.1 s/era -> 9.3 h (matches the observed 00:37 -> 09:22)
CONV 41.3 s/era -> 13.2 h
LSTM 150.4 s/era -> 48.1 h
HYBRID 154.6 s/era -> 49.5 h
Two days to tune is not a first-run experience, and it is the phase in
which the panel goes quiet, which is what made it look like a hang.
It also bought nothing. The space is 90 points (10 MA periods x 9 MA
types), so 1152 evaluations revisited each point ~13 times; and rungs of
3 and 8 eras cannot separate two MA periods at all. The 2026-08-01 run
proves it: every finalist scored 25.0-25.9% balanced accuracy - below the
33.3% one-class floor, i.e. indistinguishable noise - and the search then
"deployed the winner" of that.
THE ERROR WAS THE SCORING FUNCTION, not its constants. Using a full
training run to choose a feature's period is a wrapper method paying
wrapper prices for a decision that does not need one. The reference book
does not do this: ch. 3.3 selects inputs by measuring each candidate
indicator's CORRELATION with the target and dropping the ones with none,
with no network involved.
So: rank candidates by the MUTUAL INFORMATION between the resulting
feature vector and the triple-barrier label. MI rather than correlation
because the label is 3-class categorical and the features are not
monotonically related to it. Equal-FREQUENCY binning (rank-based),
because these features are ATR-normalised and heavy-tailed - fixed-width
bins put nearly everything in one bucket and report ~0 information for a
genuinely useful feature.
Scoring is arithmetic over the feature cache, so it costs seconds and its
cost is independent of topology: LSTM now tunes as fast as the MLP.
Coordinate sweep, not product sweep - cost is the SUM of per-parameter
candidate counts, so enabling every indicator stays affordable - with a
second pass that breaks early once nothing moves.
Sampling is IS-ONLY. Letting the OOS window influence which indicator
settings ship would mean the holdout had been used for selection and had
stopped being a holdout.
HONEST LIMIT, recorded because it is the price: MI is marginal, so a
parameter that only pays off in combination with another can be missed
(Guyon & Elisseeff 2003, filter vs wrapper). Given the wrapper it
replaces was ranking pure noise at 48 h a run, this is strictly better.
Deleted with it: GaRungEras/GaExtract/GaStore/GaMutate/GaRandomCandidate/
GaBlockCrossover/GaSortAliveByScoreDesc/GaBreedNextGeneration, 14 m_ga*
members, the GA_*/TUNE_POP_* constants, and ComputeTuneTrialBudget.
AND m_evalMode/m_evalEraBudget, because nothing set them any more - 28
read sites all permanently inert. That is not a tidy-up: the `if
(!m_evalMode)` guard on UpdateClassPriors is exactly what silently
disabled the imbalance correction for entire runs two commits ago. Dead
machinery that still reads like live machinery is this codebase's most
expensive recurring bug, and leaving 28 more instances of it would have
been indefensible.
The panel's tuning-progress state goes too - tuning no longer takes long
enough to need one.
Both builds compile 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:29:31 -04:00 | | | TP_ATR_x3 = 3, // ATR * 3 from entry
|
2026-07-22 13:33:56 -04:00 | | | TP_ATR_x4 = 4, // ATR * 4 from entry
|
| | | TP_ATR_x6 = 6, // ATR * 6 from entry
|
| | | TP_ATR_x8 = 8, // ATR * 8 from entry
|
| | | TP_ATR_x10 = 10, // ATR * 10 from entry
|
| | | };
|
2026-08-09 14:51:59 -04:00 | | | //--- RISK_REWARD_RATIO removed 2026-08-09 along with its only consumer, the Min_Risk_Reward_Ratio
|
| | | //--- input. Deleted rather than left dangling: a live enum with no input behind it is exactly the shape
|
| | | //--- of the 2026-07 incident where a saved .set kept feeding a deleted option's ordinal back in and
|
| | | //--- trained ~250 eras on the wrong target (MT5 does not validate saved enum inputs). See
|
| | | //--- Variables\Inputs.mqh for why the ratio itself had to go.
|
2026-07-13 03:23:39 -04:00 | | | enum MONEY_RISK_PERCENT_PRESET
|
| | | {
|
 perf(autotune): replace the genetic search with a filter score - hours to seconds
MEASURED COST OF THE GA, which is what retired it. Per generation:
rung 0: 8 cand x 3 seeds x 3 eras = 72 eras
rung 1: 4 cand x 3 seeds x 8 eras = 96
rung 2: 2 cand x 3 seeds x 20 eras = 120
= 288 eras/generation x 4 generations = 1152 eras BEFORE the winner's
real training began. Against the observed era times on SP500 H1:
PAI 29.1 s/era -> 9.3 h (matches the observed 00:37 -> 09:22)
CONV 41.3 s/era -> 13.2 h
LSTM 150.4 s/era -> 48.1 h
HYBRID 154.6 s/era -> 49.5 h
Two days to tune is not a first-run experience, and it is the phase in
which the panel goes quiet, which is what made it look like a hang.
It also bought nothing. The space is 90 points (10 MA periods x 9 MA
types), so 1152 evaluations revisited each point ~13 times; and rungs of
3 and 8 eras cannot separate two MA periods at all. The 2026-08-01 run
proves it: every finalist scored 25.0-25.9% balanced accuracy - below the
33.3% one-class floor, i.e. indistinguishable noise - and the search then
"deployed the winner" of that.
THE ERROR WAS THE SCORING FUNCTION, not its constants. Using a full
training run to choose a feature's period is a wrapper method paying
wrapper prices for a decision that does not need one. The reference book
does not do this: ch. 3.3 selects inputs by measuring each candidate
indicator's CORRELATION with the target and dropping the ones with none,
with no network involved.
So: rank candidates by the MUTUAL INFORMATION between the resulting
feature vector and the triple-barrier label. MI rather than correlation
because the label is 3-class categorical and the features are not
monotonically related to it. Equal-FREQUENCY binning (rank-based),
because these features are ATR-normalised and heavy-tailed - fixed-width
bins put nearly everything in one bucket and report ~0 information for a
genuinely useful feature.
Scoring is arithmetic over the feature cache, so it costs seconds and its
cost is independent of topology: LSTM now tunes as fast as the MLP.
Coordinate sweep, not product sweep - cost is the SUM of per-parameter
candidate counts, so enabling every indicator stays affordable - with a
second pass that breaks early once nothing moves.
Sampling is IS-ONLY. Letting the OOS window influence which indicator
settings ship would mean the holdout had been used for selection and had
stopped being a holdout.
HONEST LIMIT, recorded because it is the price: MI is marginal, so a
parameter that only pays off in combination with another can be missed
(Guyon & Elisseeff 2003, filter vs wrapper). Given the wrapper it
replaces was ranking pure noise at 48 h a run, this is strictly better.
Deleted with it: GaRungEras/GaExtract/GaStore/GaMutate/GaRandomCandidate/
GaBlockCrossover/GaSortAliveByScoreDesc/GaBreedNextGeneration, 14 m_ga*
members, the GA_*/TUNE_POP_* constants, and ComputeTuneTrialBudget.
AND m_evalMode/m_evalEraBudget, because nothing set them any more - 28
read sites all permanently inert. That is not a tidy-up: the `if
(!m_evalMode)` guard on UpdateClassPriors is exactly what silently
disabled the imbalance correction for entire runs two commits ago. Dead
machinery that still reads like live machinery is this codebase's most
expensive recurring bug, and leaving 28 more instances of it would have
been indefensible.
The panel's tuning-progress state goes too - tuning no longer takes long
enough to need one.
Both builds compile 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:29:31 -04:00 | | | RISK_PCT_1 = 1, // 1
|
2026-07-13 03:23:39 -04:00 | | | RISK_PCT_2 = 2, // 2
|
| | | RISK_PCT_3 = 3, // 3
|
| | | RISK_PCT_4 = 4, // 4
|
| | | RISK_PCT_5 = 5, // 5
|
| | | };
|
| | | enum BARS_EXPIRATION
|
| | | {
|
| | | BARS_X1 = 1, // 1 Candle
|
| | | BARS_X2 = 2, // 2 Candles
|
| | | BARS_X3 = 3, // 3 Candles
|
| | | BARS_X5 = 5, // 5 Candles
|
| | | BARS_X10 = 10, // 10 Candles
|
| | | BARS_X20 = 20, // 20 Candles
|
| | | };
|
2026-07-22 13:33:56 -04:00 | | | //--- Entry order placement. All ATR offsets are measured from the CURRENT price (bid/ask), NOT the
|
| | | //--- swing - this is the deliberate change for stability. Sign picks the side, magnitude is the ATR
|
| | | //--- multiple:
|
| | | //--- MARKET - fill immediately at market.
|
| | | //--- LIMIT_*xATR - pending LIMIT that many ATR on the favorable side of bid/ask (buy below /
|
| | | //--- sell above): wait for a pullback into a better price.
|
| | | //--- STOP_*xATR - pending STOP that many ATR on the breakout side of bid/ask (buy above /
|
| | | //--- sell below): enter on continuation.
|
| | | //--- ENTRY_PREV_SWING - pending order anchored at the recent swing (buy at the lookback swing low /
|
| | | //--- sell at the swing high) - the one swing-anchored option kept as a choice.
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- ENTRY_INTELLIGENT (-100) WAS REMOVED 2026-08-25 with the other confidence-scaled trade
|
| | | //--- management. It priced the ENTRY off confidence (deep pullback when unsure, market fill when
|
| | | //--- sure), which is the worst of the three places to spend an uncalibrated number: a limit that
|
| | | //--- never fills is not a smaller loss, it is a missed trade, and the misses are selected by exactly
|
| | | //--- the signal the model is least sure about - so the mode silently reshaped which setups the
|
| | | //--- strategy ever traded, not just how they were sized.
|
2026-07-22 13:33:56 -04:00 | | | //--- Non-MARKET results that clear the broker's stop-level distance become a pending order that
|
| | | //--- auto-expires after Signal_Expiration bars; anything closer just fills at market
|
| | | //--- (CExpertTrade::Buy/Sell handle the market-vs-limit-vs-stop routing off this price natively).
|
2026-07-13 03:23:39 -04:00 | | | enum ENTRY_MULTIPLIER
|
| | | {
|
2026-08-24 21:34:22 -04:00 | | | MARKET = 0, // Market order
|
 perf(autotune): replace the genetic search with a filter score - hours to seconds
MEASURED COST OF THE GA, which is what retired it. Per generation:
rung 0: 8 cand x 3 seeds x 3 eras = 72 eras
rung 1: 4 cand x 3 seeds x 8 eras = 96
rung 2: 2 cand x 3 seeds x 20 eras = 120
= 288 eras/generation x 4 generations = 1152 eras BEFORE the winner's
real training began. Against the observed era times on SP500 H1:
PAI 29.1 s/era -> 9.3 h (matches the observed 00:37 -> 09:22)
CONV 41.3 s/era -> 13.2 h
LSTM 150.4 s/era -> 48.1 h
HYBRID 154.6 s/era -> 49.5 h
Two days to tune is not a first-run experience, and it is the phase in
which the panel goes quiet, which is what made it look like a hang.
It also bought nothing. The space is 90 points (10 MA periods x 9 MA
types), so 1152 evaluations revisited each point ~13 times; and rungs of
3 and 8 eras cannot separate two MA periods at all. The 2026-08-01 run
proves it: every finalist scored 25.0-25.9% balanced accuracy - below the
33.3% one-class floor, i.e. indistinguishable noise - and the search then
"deployed the winner" of that.
THE ERROR WAS THE SCORING FUNCTION, not its constants. Using a full
training run to choose a feature's period is a wrapper method paying
wrapper prices for a decision that does not need one. The reference book
does not do this: ch. 3.3 selects inputs by measuring each candidate
indicator's CORRELATION with the target and dropping the ones with none,
with no network involved.
So: rank candidates by the MUTUAL INFORMATION between the resulting
feature vector and the triple-barrier label. MI rather than correlation
because the label is 3-class categorical and the features are not
monotonically related to it. Equal-FREQUENCY binning (rank-based),
because these features are ATR-normalised and heavy-tailed - fixed-width
bins put nearly everything in one bucket and report ~0 information for a
genuinely useful feature.
Scoring is arithmetic over the feature cache, so it costs seconds and its
cost is independent of topology: LSTM now tunes as fast as the MLP.
Coordinate sweep, not product sweep - cost is the SUM of per-parameter
candidate counts, so enabling every indicator stays affordable - with a
second pass that breaks early once nothing moves.
Sampling is IS-ONLY. Letting the OOS window influence which indicator
settings ship would mean the holdout had been used for selection and had
stopped being a holdout.
HONEST LIMIT, recorded because it is the price: MI is marginal, so a
parameter that only pays off in combination with another can be missed
(Guyon & Elisseeff 2003, filter vs wrapper). Given the wrapper it
replaces was ranking pure noise at 48 h a run, this is strictly better.
Deleted with it: GaRungEras/GaExtract/GaStore/GaMutate/GaRandomCandidate/
GaBlockCrossover/GaSortAliveByScoreDesc/GaBreedNextGeneration, 14 m_ga*
members, the GA_*/TUNE_POP_* constants, and ComputeTuneTrialBudget.
AND m_evalMode/m_evalEraBudget, because nothing set them any more - 28
read sites all permanently inert. That is not a tidy-up: the `if
(!m_evalMode)` guard on UpdateClassPriors is exactly what silently
disabled the imbalance correction for entire runs two commits ago. Dead
machinery that still reads like live machinery is this codebase's most
expensive recurring bug, and leaving 28 more instances of it would have
been indefensible.
The panel's tuning-progress state goes too - tuning no longer takes long
enough to need one.
Both builds compile 0 errors / 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:29:31 -04:00 | | | //ENTRY_PREV_SWING = -101, // Pending at previous swing low (buy) / swing high (sell)
|
2026-07-22 13:33:56 -04:00 | | | LIMIT_3xATR = -3, // Limit 3x ATR from bid/ask
|
| | | LIMIT_2xATR = -2, // Limit 2x ATR from bid/ask
|
| | | LIMIT_1xATR = -1, // Limit 1x ATR from bid/ask
|
| | | STOP_1xATR = 1, // Stop 1x ATR from bid/ask
|
| | | STOP_2xATR = 2, // Stop 2x ATR from bid/ask
|
| | | STOP_3xATR = 3, // Stop 3x ATR from bid/ask
|
2026-07-13 03:23:39 -04:00 | | | };
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- THE ORDINALS BELOW ARE PINNED, AND MUST STAY PINNED. TRAILING_STRATEGY_INTELLIGENT held value 1
|
| | | //--- until it was removed 2026-08-25; MetaTrader does not validate an enum input read back from a
|
| | | //--- saved .set or a tester optimization cache, so had the remaining members been left implicit they
|
| | | //--- would each have shifted down by one and every stored "3" would have quietly become ATR_x2
|
| | | //--- instead of ATR_x3. Explicit values keep every saved selection meaning what it meant, and leave
|
| | | //--- 1 as a hole that ValidateTradeManagementInputs() in Warrior_EA.mq5 rejects by name.
|
2026-07-13 03:23:39 -04:00 | | | enum TRAILING_STRATEGY
|
| | | {
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | TRAILING_STRATEGY_NONE = 0, // No Trailing Stop Strategy
|
| | | TRAILING_STRATEGY_ATR_x1 = 2, // ATR * 1 Trailing Strategy
|
| | | TRAILING_STRATEGY_ATR_x2 = 3, // ATR * 2 Trailing Strategy
|
| | | TRAILING_STRATEGY_ATR_x3 = 4, // ATR * 3 Trailing Strategy
|
2026-07-13 03:23:39 -04:00 | | | };
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- Same pinning rule as TRAILING_STRATEGY above: INTELLIGENT held value 1 (Kelly-criterion risk%
|
| | | //--- scaling off AI/DB confidence) and was removed 2026-08-25, so FIXED_LOT keeps its 2 rather than
|
| | | //--- inheriting the vacated 1 and turning every saved "fixed lot" chart into a risk-percent one.
|
2026-07-13 03:23:39 -04:00 | | | enum MONEY_MANAGEMENT_STRATEGY
|
| | | {
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | FIXED_RISK = 0, // Fixed risk Percent of Account
|
| | | FIXED_LOT = 2, // Fixed lot size
|
2026-07-13 03:23:39 -04:00 | | | };
|
| | |
|
| | | enum CLOSE_HOUR_OF_DAY
|
| | | {
|
| | | CLOSE_HOUR_DISABLED = -1, // Disabled
|
| | | CH_0 = 0, // 00Hxx
|
| | | CH_1 = 1, // 1Hxx
|
| | | CH_2 = 2, // 2Hxx
|
| | | CH_3 = 3, // 3Hxx
|
| | | CH_4 = 4, // 4Hxx
|
| | | CH_5 = 5, // 5Hxx
|
| | | CH_6 = 6, // 6Hxx
|
| | | CH_7 = 7, // 7Hxx
|
| | | CH_8 = 8, // 8Hxx
|
| | | CH_9 = 9, // 9Hxx
|
| | | CH_10 = 10, // 10Hxx
|
| | | CH_11 = 11, // 11Hxx
|
| | | CH_12 = 12, // 12Hxx
|
| | | CH_13 = 13, // 13Hxx
|
| | | CH_14 = 14, // 14Hxx
|
| | | CH_15 = 15, // 15Hxx
|
| | | CH_16 = 16, // 16Hxx
|
| | | CH_17 = 17, // 17Hxx
|
| | | CH_18 = 18, // 18Hxx
|
| | | CH_19 = 19, // 19Hxx
|
| | | CH_20 = 20, // 20Hxx
|
| | | CH_21 = 21, // 21Hxx
|
| | | CH_22 = 22, // 22Hxx
|
| | | CH_23 = 23, // 23Hxx
|
 feat(sessions): market-hours entry gate + "Market close" close-all option, both live from the symbol's session table
Two user requests, one authority: SymbolInfoSessionTrade, read fresh on
every call so DST and per-symbol schedule changes track themselves.
- WarriorMarketOpenNow(): CheckOpenPosition refuses entries outside the
symbol's trading sessions (Sunday reopen, index CFDs' daily breaks) -
a vote can no longer fire into a closed book and collect a broker
error. ENTRIES ONLY: exits, SL/TP and the scheduled close-all stay
unguarded - closing risk must never be blocked by a session boundary.
- CH_MARKET_CLOSE = 24 (appended, .set-safe): the close-all fires
"Close-all minute" minutes before that day's LAST session close.
Friday + Market close + xxH05 = flatten 5 minutes before Friday's
actual close. Resolved identically in three places: the live executor
(CExpertCustom::OnTick), the label walk's vertical barrier
(NextScheduledCloseAll - the symbol's CURRENT table stands in for
history; MT5 keeps none, and a fixed hour is wrong by more), and the
fingerprint (the |CUT: token already carries hour=24, so switching to
the dynamic mode re-keys the model exactly like any schedule change).
Training itself is deliberately NOT gated on market hours: weekend
compute is free and labels only ever exist on real bars - what the
session table gates is order placement and, via the close-all barrier,
what the labels may count as holdable.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 11:34:27 -04:00 | | | //--- Resolves per day from the SYMBOL'S OWN trading-session table (SymbolInfoSessionTrade),
|
| | | //--- so it follows the broker through DST and per-symbol schedules with nothing to retune:
|
| | | //--- the close-all fires "Close-all minute" minutes BEFORE that day's last session close
|
| | | //--- (e.g. minute = xxH05 -> 5 minutes before the close). The label walk resolves the same
|
| | | //--- value (Expert\AIBase\Labels.mqh), so training and the live book share one definition of
|
| | | //--- "the day ends". Explicit 24: impossible as a literal hour, appended (values are saved,
|
| | | //--- never validated - members are only ever added at the end).
|
| | | CH_MARKET_CLOSE = 24, // Market close (minus Close-all minute)
|
2026-07-13 03:23:39 -04:00 | | | };
|
| | | enum CLOSE_MINUTE_OF_HOUR
|
| | | {
|
| | | CLOSE_MINUTE_DISABLED = -1,// Disabled
|
| | | CM_0 = 0, // xxH00
|
| | | CM_5 = 5, // xxH05
|
| | | CM_10 = 10, // xxH10
|
| | | CM_15 = 15, // xxH15
|
| | | CM_20 = 20, // xxH20
|
| | | CM_25 = 25, // xxH25
|
| | | CM_30 = 30, // xxH30
|
| | | CM_35 = 35, // xxH35
|
| | | CM_40 = 40, // xxH40
|
| | | CM_45 = 45, // xxH45
|
| | | CM_50 = 50, // xxH50
|
| | | CM_55 = 55, // xxH55
|
| | | CM_60 = 60, // xxH60
|
| | | };
|
 feat(signal): make the signal cooldown tunable, and add a hard any-direction gate
The declustering the charts needed already existed - NmsLiveAccept, per-direction
run-collapse plus cross-direction resolution plus strict alternation - and it was
already set to 10 bars. It could not be TUNED: SignalClusterWindow was a compile-
time const, so finding the right value needed a rebuild. That is the actual gap.
Now three inputs, as enum dropdowns:
Signal_CooldownScope per-direction, or a hard any-direction gate on top
Signal_CooldownBars SCB_OFF..SCB_50, default 10
Signal_CooldownMinutes SCM_OFF..SCM_1440, overrides bars when set
Minutes resolve against the CHART period and round UP, so a cooldown asked for in
wall-clock is never silently shorter than requested and survives a timeframe
change.
SCB_/SCM_ prefixes are deliberately unique. M15/M30/M60 are ALREADY members of
NF_LOOKBACK_PRESETS, and MQL5 binds a duplicated enum member to the first-declared
enum silently - the obvious names would have compiled straight into the news
filter's values.
THE ANY-DIRECTION GATE IS ADDITIVE, NOT A REPLACEMENT, and the first cut of this
had it backwards. Measured on the live log: the current rules draw 222 arrows over
4999 bars, while a BARE 10-bar cooldown permits up to 454 - because ALTERNATION is
what declutters today, not the window. Swapping the rules out would have roughly
doubled the clutter it was asked to remove. Layered, it can only ever suppress
more. Suppressed bars still advance the per-direction last-SEEN cursors, so a run
straddling the boundary does not restart as if it were fresh.
Applied at all THREE sites that must agree - live inference, OOS pass-3 scoring
and the chart renderer. Their own comments say why: an arrow set that does not
obey the same rule as the traded set shows calls the EA would never take.
Also corrects a stale comment that called this window "display only". It is not:
when it suppresses, the live path zeroes the signal outright - no arrow, no vote,
no position. Training never sees it, so these cost no retrain and are correctly
absent from the fingerprint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 09:48:28 -04:00 | | | //--- WHAT A KEPT SIGNAL BLOCKS. Member names are deliberately long and unique: MQL5 resolves a name
|
| | | //--- duplicated across two enums to the FIRST-DECLARED one, silently, and this project has already
|
| | | //--- shipped a wrong target that way.
|
| | | //--- Dropdown presets for the cooldown. Prefixes SCB_/SCM_ are deliberately unique: MQL5 resolves a
|
| | | //--- duplicated enum member to the FIRST-DECLARED enum, silently - and M15/M30/M60 are ALREADY taken
|
| | | //--- by NF_LOOKBACK_PRESETS below, so the obvious names would have bound to the news filter's values.
|
| | | enum SIGNAL_COOLDOWN_BARS
|
| | | {
|
| | | SCB_OFF = 0, // Off (no cooldown)
|
| | | SCB_2 = 2, // 2 bars
|
| | | SCB_3 = 3, // 3 bars
|
| | | SCB_5 = 5, // 5 bars
|
| | | SCB_8 = 8, // 8 bars
|
| | | SCB_10 = 10, // 10 bars
|
| | | SCB_15 = 15, // 15 bars
|
| | | SCB_20 = 20, // 20 bars
|
| | | SCB_30 = 30, // 30 bars
|
| | | SCB_50 = 50, // 50 bars
|
| | | };
|
| | | enum SIGNAL_COOLDOWN_MINUTES
|
| | | {
|
| | | SCM_OFF = 0, // Use the bar count instead
|
| | | SCM_15 = 15, // 15 minutes
|
| | | SCM_30 = 30, // 30 minutes
|
| | | SCM_60 = 60, // 1 hour
|
| | | SCM_120 = 120, // 2 hours
|
| | | SCM_240 = 240, // 4 hours
|
| | | SCM_480 = 480, // 8 hours
|
| | | SCM_720 = 720, // 12 hours
|
| | | SCM_1440 = 1440, // 1 day
|
| | | };
|
| | | enum SIGNAL_COOLDOWN_SCOPE
|
| | | {
|
| | | //--- A kept Buy silences nearby Buys only, plus cross-direction flicker resolution and strict
|
| | | //--- Buy/Sell alternation. Thins RUNS but still permits a fresh alternating pair every window.
|
| | | SIGNAL_COOLDOWN_PER_DIRECTION = 0, // Per direction (collapse runs + alternate)
|
| | | //--- ADDS a hard any-direction cooldown ON TOP of the three rules above. Deliberately additive and
|
| | | //--- not a replacement: measured on the live log, ALTERNATION is what declutters today (222 arrows
|
| | | //--- over 4999 bars), while a BARE 10-bar cooldown permits up to 454 - so swapping the rules out
|
| | | //--- would have roughly DOUBLED the clutter it was asked to remove. Layered, it can only ever
|
| | | //--- suppress more, never less.
|
| | | SIGNAL_COOLDOWN_ANY_SIGNAL = 1, // Any signal, on top of per-direction (fewest signals)
|
| | | };
|
2026-07-13 03:23:39 -04:00 | | | enum CLOSE_DAY_OF_WEEK
|
| | | {
|
| | | CLOSE_DAY_DISABLED = -1, // Disabled
|
| | | CLOSE_MONDAY = 1, // Monday
|
| | | CLOSE_TUESDAY = 2, // Tuesday
|
| | | CLOSE_WEDNESDAY = 3, // Wednesday
|
| | | CLOSE_THURSDAY = 4, // Thursday
|
| | | CLOSE_FRIDAY = 5, // Friday
|
| | | CLOSE_EVERYDAY, // Every Day
|
| | | };
|
| | | enum NF_LOOKBACK_PRESETS
|
| | | {
|
| | | NF_DISABLED = -1, // Disabled
|
| | | M5 = 5, // 5 Minutes
|
| | | M15 = 15, // 15 Minutes
|
| | | M30 = 30, // 30 Minutes
|
| | | M45 = 45, // 45 Minutes
|
| | | M60 = 60, // 1 Hour
|
| | | M120 = 120, // 2 Hours
|
| | | M240 = 240, // 4 Hours
|
| | | };
|
| | | enum NF_IMPACT_PRESETS
|
| | | {
|
| | | HOLIDAYS = 0, //Holidays
|
| | | LOW = 1, // Low Impact News
|
| | | MEDIUM = 2, // Medium Impact News
|
| | | HIGH = 3, // High Impact News
|
| | | };
|
 feat(direction): INTELLIGENT trade direction - the measured drift picks the side(s)
SQX EdgeFinder precedent (user request): adjust for the drift instead of
fighting it. The 2026-08-19 telemetry found the models leaning SHORT
(Buy recall 21% vs Sell 40%) against a long-favored market (always-long
34.3% vs always-short 29.5% at the adopted geometry).
TRADING_DIRECTION gains INTELLIGENT = 3 (appended, explicit value,
.set-safe). It resolves at runtime from the label cache's per-side win
rates - the Buy/Sell shares ARE the win rates of taking every bar
long/short at the REAL stop/target with spread charged. A side is
dropped only when BOTH hold: the drift gap clears 2 combined SEs on the
overlap-deflated effective sample (EffectiveSampleSize - labels overlap
~18x), AND the weaker side sits below cost-adjusted break-even (a side
that still clears costs is kept; drift tilt alone is not a reason to
refuse a profitable side). Fails open to BOTH: unmeasured, tiny
effective n (<30), insignificant gap, or classic-only charts (no label
cache).
One resolution point - WarriorEffectiveDirection() - feeds all three
gates so they cannot drift apart: CheckOpenLong/Short (live entries),
the filtered-view sweep (a blocked side falls into the delete branch,
mirroring live), and the vote HUD's "-> TRADE" verdict. The verdict
re-derives at every label-cache rebuild, prints only on change, and is
computed even when the input is not Intelligent (marked informational).
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 11:10:05 -04:00 | | | //--- INTELLIGENT added 2026-08-19 (user request, SQX EdgeFinder precedent: "adjust for the drift
|
| | | //--- to increase success rate"). It resolves to LONG_ONLY / SHORT_ONLY / BOTH at runtime from the
|
 feat(target): delete the barrier/geometry stack - the label is the verdict
Step 3 of the swing-pivot plan, whole-hog. The swing label is now the ONE
target and the era verdict is precision + recall per class against the
label's own base rate - no win rate, no break-even, no expectancy, no
geometry anywhere in training.
DELETED
- Expert/Excursion/ (4), Expert/BarrierHorizon/ (4), GeometrySweep,
FirstPassageLadder, Labeling/TripleBarrier.mqh (CLabelOverlap survives
in Labeling/LabelOverlap.mqh), 3 test EAs.
- TripleBarrierLabel + walk, fractal label, geometry derivation/scan/
adoption, exit-policy replay, excursion MI targets, the drift verdict
(DIRECTION_INTELLIGENT), the recall floor, balanced-accuracy telemetry,
the barrier defines, the .cfg geometry adopt (slots kept as zeros for
the positional layout), the derived-geometry live-order override.
- TRAINING_TARGET input/enum: direction models are always swing; META2
re-keys the meta head onto label agreement (descriptor loses its two
geometry slots).
REWORKED
- Labels.mqh (1795 -> ~370 lines): AdvanceSwingLabelState with
FINALITY-GATED CACHING - an unresolved bar (pivot pair uncommitted) is
never cached, so it can never freeze as a false Neutral; training,
calibration, OOS scoring and online learning all skip unresolved bars.
- SDeployVerdict: significance-only; SOosTally chance = larger
directional class share; pooled gate poolability = timeframe (record v2).
- Purge/embargo/declustering gaps: the measured mean label resolution
lag (LabelResolutionBars), not a barrier horizon.
- Pool purge key + backfill DB rows: marked at the bar the label
resolved on (m_labelResolveAge), not a fabricated barrier touch.
- Online learning frontier: finality, not a horizon delay.
- m_bestBalancedOos -> m_bestSelectionScore, m_erasSinceBestBalanced ->
m_erasSinceBest, ensemble vote outcome arrays -> label arrays.
STEP 4 folded in: Entry_Multiplier / SL_Mode / TP_Mode / tradingdirection
are inputs again - trade management is the tester GA's search space.
Fingerprints: every direction model re-keys (TGT:SWG1 now unconditional,
CUT token gone); META1 -> META2. Full retrain, as planned.
Compile-verified in _claude_stage: Warrior_EA + both surviving test EAs,
0 errors, 0 warnings each.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:42:31 -04:00 | | | //--- The Intelligent member (value 3, the measured drift verdict) went with the barrier stack
|
| | | //--- 2026-08-24: its data source was the per-side win caches. A saved .set holding 3 falls outside
|
| | | //--- the enum and MT5 clamps it, which is the visible failure a silent re-map would not be.
|
2026-07-13 03:23:39 -04:00 | | | enum TRADING_DIRECTION
|
| | | {
|
 fix(build): four compile faults - one was a SILENT enum collision that inverted the direction policy
Reported by the user's MetaEditor compile of f64e0f8 (26 errors, 4 warnings). The four
warnings mattered more than the errors.
1. INTELLIGENT WAS TWO ENUMS. MONEY_MANAGEMENT_STRATEGY::INTELLIGENT (=1) is declared
BEFORE TRADING_DIRECTION::INTELLIGENT (=3) in InputEnums.mqh, so MQL5 resolved every
'tradingdirection == INTELLIGENT' to the MM member and converted it to value 1 =
TRADING_DIRECTION::LONG_ONLY. Wrong in both directions at once: selecting Intelligent
(3) matched NOTHING and silently traded both sides, while selecting Long only (1)
matched and handed the decision to the measured drift verdict - which can answer
SHORT_ONLY, so the one setting that must never go short could have. Reported by the
compiler as a WARNING only, never an error. Renamed to DIRECTION_INTELLIGENT; the
VALUE stays 3, so saved .set files are unaffected. Swept every enum in the repo for
sibling collisions (38 enums, detector validated against the pre-fix source, which it
flags): none remain.
2. g_warriorMetaGate sits above the class it points at - added the forward declaration,
the same pattern g_warriorEnsemble already uses in ExpertSignalAIBase.mqh.
3. The broker-time rename (b63e39f) never reached BufferNewTickSignal's PARAMETER or its
two call sites: the local became brokerTime, the parameter stayed gmtTime, and the
body was rewritten to read brokerTime. All five sites now agree.
4. ConfigureAISignal calls IsMetaTarget() from a free function - moved it to the public
section (identity, not an implementation seam); the other meta seams stay protected.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:16:45 -04:00 | | | BOTH, // Allow both long and short trades
|
| | | LONG_ONLY, // Allow only long (buy) trades
|
| | | SHORT_ONLY, // Allow only short (sell) trades
|
2026-07-13 03:23:39 -04:00 | | | };
|
| | |
|
 refactor(stdlib): the vote thresholds are ints on the library's scale, not "confidence %"
The MECHANISM was already stdlib and is untouched: ThresholdOpen() ->
m_threshold_open, tested as `m_direction >= m_threshold_open` exactly as
CExpertSignal does it. What was wrong was the presentation. Both inputs
were preset ENUMS labelled "Min confidence to open/close (%)", which
names the wrong quantity - m_direction is a WEIGHTED MEAN OF PATTERN
WEIGHTS, not a probability, and nothing in this path is a confidence.
They are now plain ints named the way the MQL5 wizard names them:
input int Signal_ThresholdOpen = 25; // [0...100]
input int Signal_ThresholdClose = 101; // [0...100, 101 = never]
Values are exactly what shipped, so behaviour is unchanged. 101 rather
than the library's default of 100 for close: a weighted mean of pattern
weights cannot REACH 101, which is how the shipped config disables the
vote exit, and quietly lowering it to 100 would re-arm a live exit route
as a side effect of a naming change.
VOTE_CLOSE_PRESETS is deleted (its only user is gone). PERCENTAGE_PRESETS
stays - MinRecall genuinely is a percentage.
** ACTION NEEDED ON DEPLOYED CHARTS: the inputs are RENAMED, so saved
.set files no longer match and charts fall back to the defaults above.
Those defaults are the current shipped values, so a chart on 25/Disabled
needs nothing; a tuned one does.
Comment cleanup in the same pass, and this part was not cosmetic - three
blocks documented mechanisms that no longer exist:
- the AI early-exit route (deleted in 38a12a2) described as live and
still firing every bar;
- the m_lastNonNeutralSignal alternation gate (removed 2026-08-01)
described as consuming the AI's vote;
- 16 lines of VOTE_CLOSE_PRESETS documentation orphaned by that enum's
deletion, ending with "see that enum's note directly above" pointing
at nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 08:39:01 -04:00 | | | //--- 5-POINT STEPS BELOW 50, 10-POINT ABOVE (2026-08-19). This is Signal_ThresholdOpen's scale, and under
|
2026-08-19 08:25:49 -04:00 | | | //--- CONSENSUS arithmetic the votes it must separate are quantized by AGREEMENT: with four members
|
| | | //--- whose tiers self-rank to pooled win rates ~29, unanimity reads ~29, 3-of-4 ~22, 2-of-4 ~14.5.
|
| | | //--- The old 10-point grid straddled every rung the ensemble can express - 20 admitted 3-of-4 and
|
| | | //--- 30 admitted nothing - so the thresholds an operator actually wants, which sit BETWEEN rungs,
|
| | | //--- did not exist on the dropdown. Steps stay coarse above 50 because nothing reachable lives up
|
| | | //--- there until pooled skill does. Members are ADDED, never removed or renumbered: MT5 saves the
|
| | | //--- VALUE and does not validate it against the current enum (see the RISK_LIMIT_PCT_PRESET
|
| | | //--- removal note at the bottom of this file), so adding explicit-valued members is .set-safe
|
| | | //--- while deleting one is the trained-250-eras-on-the-wrong-target failure.
|
2026-07-13 03:23:39 -04:00 | | | enum PERCENTAGE_PRESETS
|
| | | {
|
2026-08-19 08:25:49 -04:00 | | | PCT_5 = 5, // 5%
|
2026-07-13 03:23:39 -04:00 | | | PCT_10 = 10, // 10%
|
2026-08-19 08:25:49 -04:00 | | | PCT_15 = 15, // 15%
|
2026-07-13 03:23:39 -04:00 | | | PCT_20 = 20, // 20%
|
2026-08-19 08:25:49 -04:00 | | | PCT_25 = 25, // 25%
|
2026-07-13 03:23:39 -04:00 | | | PCT_30 = 30, // 30%
|
2026-08-19 08:25:49 -04:00 | | | PCT_35 = 35, // 35%
|
2026-07-13 03:23:39 -04:00 | | | PCT_40 = 40, // 40%
|
2026-08-19 08:25:49 -04:00 | | | PCT_45 = 45, // 45%
|
2026-07-13 03:23:39 -04:00 | | | PCT_50 = 50, // 50%
|
| | | PCT_60 = 60, // 60%
|
| | | PCT_70 = 70, // 70%
|
| | | PCT_80 = 80, // 80%
|
| | | PCT_90 = 90, // 90%
|
| | | PCT_100 = 100, // 100%
|
| | | };
|
 feat(trade): two books per symbol, and delete the vote exit
Allow_Hedging (default ON, live only on a RETAIL_HEDGING account) gives the EA
an independent long book and short book on its 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. On a netting account, or with the input off, the original
single-position path runs bit-for-bit unchanged and init says which one is live.
WHY THIS INSTEAD OF A VOTE EXIT. The deploy gate certifies
P(label agrees | vote fired) and the label runs to the barrier, so closing early
on a reversal makes the realised outcome stop being the labelled one - the
certified precision no longer describes what is traded. Opening the other side
acts on the new signal and leaves the old position's certification intact, and
costs no more than reversing: both pay the new side's spread, the difference is
only that the existing position runs on to a barrier already measured as
positive-expectancy. So Signal_ThresholdClose is DELETED rather than tuned,
along with its SIGNAL_CLOSE_PRESETS enum; the threshold is pinned to an
arithmetically unreachable 101 (the stock default of 100 is reachable by a
weighted mean of values capped at 100).
Note the two books can never both fill from one signal: CheckOpenLong and
CheckOpenShort test opposite signs of the same m_direction, so at most one clears
per tick. A hedge only forms when a LATER opposite vote fires - which is what
keeps it from being a guaranteed-loss wash pair.
The mechanism is a SelectPosition() override keyed on the active book's magic;
every inherited close/trail path then operates on that book untouched. The long
book keeps Expert_MagicNumber, so no existing position, journal row or
risk-budget state file is re-addressed. Short book is +1.
Four ownership filters had to widen from "== m_magic" to WarriorOwnsMagic(),
or the short book would have been invisible to the code that must reach it:
the scheduled close-all (positions and orders), the risk budget's emergency
flatten, and the journal's MAE/MFE walk. WarriorOwnsMagic() is deliberately NOT
gated on Allow_Hedging - turning the input off while a short-book position is
open would otherwise orphan it with nothing left to close it.
Risk sizing needed no change: CapRiskAmount already subtracts OpenRiskAtStops(),
which counts every position regardless of magic, so the second book is sized
inside what the first one left. Conservative for a hedged pair, which cannot
lose both stops - the safe direction.
Retrain-neutral: neither input is in BuildModelFingerprint() or
ComputeDbConfigFingerprint(). Compiled clean; NOT yet run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:20:35 -04:00 | | | //--- SIGNAL_CLOSE_PRESETS WAS REMOVED 2026-08-26 with the Signal_ThresholdClose input it existed for.
|
| | | //--- The vote exit is gone rather than disabled-by-default: a vote-driven early close trades a horizon
|
| | | //--- the deploy gate never certified, and acting on a reversal is Allow_Hedging's job now (it opens
|
| | | //--- the other book instead of closing this one). The disabling value survives as
|
| | | //--- VOTE_EXIT_DISABLED_THRESHOLD in Variables\Inputs.mqh, which is what the signal is pinned to.
|
| | | //--- Nothing else referenced the enum, so no ordinal moved - see the flat-namespace warning above,
|
| | | //--- which is why the members were prefixed in the first place.
|
 refactor(ai): derive the first dense layer's width instead of asking for it
InitialNeurons was an input whose only defensible value depends on two
things the user cannot see when picking from a dropdown: how wide the input
vector ended up after feature selection, and how much in-sample data the
study period actually yields. Left to a hand-picked constant it was badly
wrong - 500 units against a 420-wide input is 210,500 weights, 72% of a
292,583-weight model, against ~36,500 training bars of which only ~2,236
are directional. That is 6.6 weights per training bar, and it EXPANDS a set
of highly correlated inputs rather than compressing them.
The symptom was already in the logs and had been read as a depth problem:
the shallowest topology consistently beat the deepest (perceptron 52.7%
balanced, hybrid 41.3%). Over-parameterization predicts that ordering just
as well as covariate shift does, and only one of the two had been addressed.
ComputeFirstLayerWidth() budgets roughly one first-layer weight per
in-sample bar. Measured across the configurations in use:
M15 10y -> 256 units, 129,071 weights, 0.73 per bar
H1 10y -> 64 units, 28,727 weights, 0.65 per bar
H4 10y -> 16 units, 7,559 weights, 0.68 per bar
Two design points that matter:
- It estimates in-sample bars from the STUDY PERIOD and timeframe, not
from Bars(). What is downloaded grows over a terminal's lifetime, and a
topology that widened as history filled in would re-key its own weights
file and discard a trained model.
- The result is snapped down to a coarse power-of-two ladder, so the
estimate would have to be wrong by ~2x to change the answer.
Every field it reads is already part of the weights-filename fingerprint,
so the derived value needs no fingerprint entry of its own. The public
setter is removed - it could only have been called after construction, and
would either be ignored or silently re-key the model mid-run.
Where the data cannot support even the floor (D1 over 10 years is under
2,000 bars) it now says so and names the fixes, rather than quietly
training a model with more weights than examples.
The DB config fingerprint drops the term too, which re-keys existing
pattern databases once - correct, since a model an order of magnitude
smaller should not inherit the old one's win-rate history.
Compiles 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 13:01:16 -04:00 | | | //--- FIRST_LAYER_NEURONS removed 2026-07-29. The first dense layer dominates the parameter count -
|
| | | //--- it is (inputWidth+1) x width - so its only defensible value is a function of the input width and
|
| | | //--- the amount of in-sample data, neither of which the user can see when picking from a dropdown. It
|
| | | //--- is now derived: see CExpertSignalAIBase::ComputeFirstLayerWidth().
|
 feat(ensemble): per-NN inputs replace the preset selector - the meta head becomes the vote's gate
User design (2026-08-19): 'remove the enum menu that selects neural networks... individual
inputs for every NN just like classic signals... the META NN should be integrated into the
voting decision pipeline when enabled... as a bonus meta labelling is applied to enabled NNs.'
- AI_CHOICE is GONE (tombstoned per the stale-.set doctrine). Use_MLP/Use_CONV/Use_LSTM/
Use_CONVLSTM are ordinary bools like the classic votes; the ensemble arithmetic adapts to
any subset because the consensus divisor is the enabled capable weight. Two or more
enabled = ensemble (|ENS1 token + joint gate, exactly the old AI_HYBRID fingerprints, so
existing weight files keep loading); one = the old solo preset; none = classic-only.
- Use_MetaLabeling un-couples META from the direction NNs (the old selector made them
mutually exclusive). S3 ships: CSignalMETA::LiveMetaGate scores each vote-cleared entry
(shared window at bar 1 + proposal descriptor: side, net vote, live geometry, spread/ATR;
pattern one-hot ZEROED - ranking, not calibrated probability, documented in the body) and
vetoes below the cost-adjusted break-even. Entries only; fail-open everywhere, loudly.
- COEXISTENCE HAZARDS closed: VoteCapableWeight()=0 and ProspectiveVote()=false for the
meta target - solo-only until today, a trained META would otherwise sit in the consensus
divisor as a permanent abstainer and shrink every vote by its module weight.
- CERTIFIED == TRADED: the ensemble era verdict replays the identical veto through the same
g_warriorMetaGate pointer over its OOS fired bars (bar re-resolved from the row's own
time; fail-open counted as fires and reported: 'metaGate: N approved, M vetoed, K
unscored'). The overlay deliberately does NOT replay it (veto-filter-in-replay class,
calendar-cliff precedent) - documented at the sweep site. Solo charts' own gate does not
model the veto - the standing solo-gate caveat, documented at the input.
- DB continuity: the pattern/journal DB fingerprint's first slot was (int)AIType;
DbLegacyAiSlot() maps every legacy-expressible config to its OLD value (new 2-3 member
subsets get 100+bitmask, outside the legacy range) so no existing database re-keys.
filterID becomes the enabled roster via one EnabledNNSummary().
- HUD: the meta line shows the gate (armed/(trn), last P vs BE, ok/veto tally); the
armed/disarmed announcement fires on state change via one latch (MetaGateArmedNow), not
only when an entry happens to be proposed.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:01:02 -04:00 | | | //--- Architecture-aware dense-topology presets were then folded into the (since-removed) AI_CHOICE
|
| | | //--- selector; today the front-end choice is the per-NN Use_* toggles and the dense taper is fully
|
| | | //--- derived - see ComputeHiddenLayerCount.
|
2026-07-22 22:51:04 -04:00 | | | //--- LSTM's own recurrent hidden-unit count - previously silently piggybacked on HiddenLayersCount
|
| | | //--- (an unrelated dense-taper-depth setting), which meant it could never be tuned independently and
|
| | | //--- defaulted to a value (4) nobody actually chose on purpose. Decoupled into its own input.
|
| | | enum LSTM_HIDDEN_SIZE_PRESET
|
| | | {
|
| | | LSTM_HIDDEN_8 = 8, // 8 Units
|
| | | LSTM_HIDDEN_16 = 16, // 16 Units
|
| | | LSTM_HIDDEN_32 = 32, // 32 Units
|
| | | LSTM_HIDDEN_64 = 64, // 64 Units
|
| | | LSTM_HIDDEN_128 = 128, // 128 Units
|
| | | };
|
| | | //--- CONV's own output-filter count for its convolutional layer - previously silently piggybacked on
|
| | | //--- HiddenLayersCount too (same bug class as LstmHiddenSize above), defaulting to a bottleneck of 4
|
| | | //--- filters/bar. Decoupled into its own input.
|
| | | enum CONV_FILTER_COUNT_PRESET
|
| | | {
|
| | | CONV_FILTERS_8 = 8, // 8 Filters
|
| | | CONV_FILTERS_16 = 16, // 16 Filters
|
| | | CONV_FILTERS_32 = 32, // 32 Filters
|
| | | CONV_FILTERS_64 = 64, // 64 Filters
|
| | | CONV_FILTERS_128 = 128, // 128 Filters
|
| | | };
|
2026-07-27 22:08:55 -04:00 | | | //--- Shared pooling shape for the Conv front-end used by both CONV and HYBRID. Keeping this
|
| | | //--- separate from ConvFilterCount lets the filter-bank width and the downsampling span be tuned
|
| | | //--- independently, instead of smuggling one into the other.
|
 fix(ai): drop the conv pooling stage - it reduced across filters, not time
FeedForwardConv emits POSITION-MAJOR output, matrix_o[out + window_out * i],
so one bar's window_out filter responses are contiguous and consecutive bars
sit window_out apart. Both pooling implementations (FeedForwardProof and
CPU_FeedForwardProof) slide FLAT over that buffer - pos = i * step, reducing
`window` CONSECUTIVE elements. On a position-major layout those neighbours
are different FILTERS of the same bar, never one filter across time.
At the shipped 3/2 the pool computed max(bar0_f0, bar0_f1, bar0_f2), then
max(bar0_f2, bar0_f3, bar0_f4), with every 8th window straddling a bar
boundary. So it collapsed unrelated feature detectors into whichever fired
hardest, passed gradient to that winner only, and halved the feature map
while doing it - all below every learnable layer, where nothing above can
recover it. The removed inputs' own labels ("3 Bars") show time-axis pooling
was the intent throughout.
Measured cost: CONV sat pinned at ~40% balanced accuracy for 510 eras with
Sell recall 0%, while plain MLPs on the same data reached 57-61%. HYBRID,
which also carried this stage, came second-worst of the batch-norm group.
Not fixable in the topology: pooling one filter across time needs a stride
of window_out BETWEEN samples within a window, which a consecutive-window
kernel cannot express at any window/step. That needs a stride-aware kernel
in Network.cl + WarriorCPU.cpp + WarriorDML.cpp and a DLL rebuild, and is
only worth doing if a conv front-end earns its place without downsampling
first - with 20 sliding positions there is little to gain by halving them.
ConvPoolWindow/ConvPoolStep and their enums are removed with it, along with
the |CP: fingerprint term added earlier today.
Both builds compile 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 19:28:44 -04:00 | | | //--- CONV_POOL_WINDOW_PRESET / CONV_POOL_STEP_PRESET removed 2026-07-29 along with the pooling
|
| | | //--- stage itself - their "N Bars" labels described time-axis pooling the implementation could
|
| | | //--- never perform. See AddConvStage() in Expert\ExpertSignalAIBase.mqh.
|
2026-07-13 03:23:39 -04:00 | | |
|
| | | enum MIN_NEURONS_COUNT
|
| | | {
|
| | | MIN_NEURONS_10 = 10, // Min. 10 Neurons per layer
|
| | | MIN_NEURONS_20 = 20, // Min. 20 Neurons per layer
|
| | | MIN_NEURONS_30 = 30, // Min. 30 Neurons per layer
|
| | | MIN_NEURONS_40 = 40, // Min. 40 Neurons per layer
|
| | | MIN_NEURONS_50 = 50, // Min. 50 Neurons per layer
|
| | | };
|
2026-07-16 00:56:33 -04:00 | | | // Value IS the reduction percentage applied per hidden layer (retention = 100-value), consumed
|
| | | // via BuildFreshTopology()'s n = n*((100-value)*0.01) taper - e.g. RF_70 keeps 30% of the previous
|
| | | // layer's neurons, i.e. a genuine 70% reduction per layer, matching the label at face value.
|
2026-07-13 03:23:39 -04:00 | | | enum NEURONS_REDUCTION_FACTOR
|
| | | {
|
2026-07-16 00:56:33 -04:00 | | | RF_10 = 10, // 10 % Neurons Reduction Per Layer
|
| | | RF_20 = 20, // 20 % Neurons Reduction Per Layer
|
| | | RF_30 = 30, // 30 % Neurons Reduction Per Layer
|
| | | RF_40 = 40, // 40 % Neurons Reduction Per Layer
|
2026-07-13 03:23:39 -04:00 | | | RF_50 = 50, // 50 % Neurons Reduction Per Layer
|
2026-07-16 00:56:33 -04:00 | | | RF_60 = 60, // 60 % Neurons Reduction Per Layer
|
| | | RF_70 = 70, // 70 % Neurons Reduction Per Layer
|
| | | RF_80 = 80, // 80 % Neurons Reduction Per Layer
|
| | | RF_90 = 90, // 90 % Neurons Reduction Per Layer
|
2026-07-13 03:23:39 -04:00 | | | };
|
| | | enum OUTPUT_NEURONS_COUNT
|
| | | {
|
| | | OUTPUT_REGRESSION = 1, // Regression Algorithm
|
| | | OUTPUT_CLASSIFICATION = 3, // Classification Algorithm
|
| | | };
|
 feat(ensemble): per-NN inputs replace the preset selector - the meta head becomes the vote's gate
User design (2026-08-19): 'remove the enum menu that selects neural networks... individual
inputs for every NN just like classic signals... the META NN should be integrated into the
voting decision pipeline when enabled... as a bonus meta labelling is applied to enabled NNs.'
- AI_CHOICE is GONE (tombstoned per the stale-.set doctrine). Use_MLP/Use_CONV/Use_LSTM/
Use_CONVLSTM are ordinary bools like the classic votes; the ensemble arithmetic adapts to
any subset because the consensus divisor is the enabled capable weight. Two or more
enabled = ensemble (|ENS1 token + joint gate, exactly the old AI_HYBRID fingerprints, so
existing weight files keep loading); one = the old solo preset; none = classic-only.
- Use_MetaLabeling un-couples META from the direction NNs (the old selector made them
mutually exclusive). S3 ships: CSignalMETA::LiveMetaGate scores each vote-cleared entry
(shared window at bar 1 + proposal descriptor: side, net vote, live geometry, spread/ATR;
pattern one-hot ZEROED - ranking, not calibrated probability, documented in the body) and
vetoes below the cost-adjusted break-even. Entries only; fail-open everywhere, loudly.
- COEXISTENCE HAZARDS closed: VoteCapableWeight()=0 and ProspectiveVote()=false for the
meta target - solo-only until today, a trained META would otherwise sit in the consensus
divisor as a permanent abstainer and shrink every vote by its module weight.
- CERTIFIED == TRADED: the ensemble era verdict replays the identical veto through the same
g_warriorMetaGate pointer over its OOS fired bars (bar re-resolved from the row's own
time; fail-open counted as fires and reported: 'metaGate: N approved, M vetoed, K
unscored'). The overlay deliberately does NOT replay it (veto-filter-in-replay class,
calendar-cliff precedent) - documented at the sweep site. Solo charts' own gate does not
model the veto - the standing solo-gate caveat, documented at the input.
- DB continuity: the pattern/journal DB fingerprint's first slot was (int)AIType;
DbLegacyAiSlot() maps every legacy-expressible config to its OLD value (new 2-3 member
subsets get 100+bitmask, outside the legacy range) so no existing database re-keys.
filterID becomes the enabled roster via one EnabledNNSummary().
- HUD: the meta line shows the gate (armed/(trn), last P vs BE, ok/veto tally); the
armed/disarmed announcement fires on state change via one latch (MetaGateArmedNow), not
only when an entry happens to be proposed.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:01:02 -04:00 | | | //--- AI_CHOICE REMOVED 2026-08-19 (user request: "remove the enum menu that selects neural networks,
|
| | | //--- add individual inputs for every NN just like classic signals"). The preset selector could only
|
| | | //--- express solo-or-all (no 2-3 member subsets) and made the META head mutually exclusive with the
|
| | | //--- direction NNs. Replaced by the per-NN bools in Variables\Inputs.mqh (Use_MLP/Use_CONV/Use_LSTM/
|
 refactor(meta): remove meta-labeling entirely - RETRAIN-NEUTRAL
~2,300 lines. META had real, repeatedly measured ranking skill and ZERO
operating points that ever cleared break-even (0/350 H1 eras, 1/999 H4
pre-2-sigma, 0/8 pooled fitted points). The clinching arithmetic was edge x
width = 0.095 ATR/trade against spread 0.099 ATR/trade, and the
dose-response showed the high-conviction tail is temporally unstable -
the precision-vs-threshold slope flips sign between calib and test on 3 of
4 symbols, so no ex-ante threshold rule exists. It shipped default-off and
never gated a live entry. The self-measured tier weights are what actually
rank the vote, and all six H4 instruments converged on them alone.
RETRAIN-NEUTRAL, and that is the property that made this safe:
- The weights fingerprint emitted "|TGT:META2" or "|TGT:SWG1" from an
if/else. Every direction model already took the SWG1 arm, so
collapsing it to an unconditional append is byte-identical. No .nnw or
.cfg is orphaned or re-keyed.
- NetInputWidth() lost its "+ MetaDescWidth()" term. MetaDescWidth()
returned 0 for every direction model, so the input layer is unchanged.
- DbLegacyAiSlot()'s slot 5 was reachable only with all four Use_* NNs
off AND meta on - a config that never shipped. Every existing .db keeps
its filename.
Deleted outright: Signals/SignalMETA.mqh, Expert/Trading/MetaGate.mqh (the
directory is now empty), Expert/Training/{MetaCorpus,MetaCandidateStore,
MetaFamilies}.mqh, Tests/Test_MetaFamilies.mq5, Meta_Labeling_Design.md.
Unwound in place, the delicate part: Training.mqh carried four
IsMetaTarget() branches whose else-arm WRAPPED the direction body (pass 1
queueing, pass 2 backprop, pass 2.5 calibration, pass 3 OOS scoring). Each
wrapper is removed and the direction body promoted back to its original
nesting - the bodies were never re-indented when the wrappers were added,
so the promoted code is byte-identical to what ran before META existed.
Also gone: the ensemble verdict's meta-veto replay and its
approved/vetoed/unscored counters, the per-family/per-side OOS
decomposition arrays, the m_isTrainQueueCand parallel queue and its
lockstep shuffle, and the S2 era report.
Also removed: the CMetaGate abstraction and the live CheckOpenPosition
veto; m_gates plus AddFilter's non-voter routing and IsVotingSignal()
(META was the only non-voting child, so m_gates was always empty);
m_parentSignal/SetParentSignal (existed only to reach the root's gate);
SweepPrepare/SweepPrepareIndicator (only caller was the corpus sweep);
IsMetaTarget() from all four view interfaces and their adapters;
Use_MetaLabeling, EnableMETA, Meta_ExportDataset, m_trainTarget.
EvalShift is KEPT - HistoricalNetVote() uses it for the filtered overlay,
not just the corpus sweep; only its comment changed. The 2-output softmax
arm in NetForward.mqh is kept too: it costs nothing and is the reusable
binary-head path, now commented as unclaimed rather than as META's.
Compile-verified in _claude_stage: 0 errors, 0 warnings, matching the
pre-edit baseline.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 09:44:52 -04:00 | | | //--- Use_CONVLSTM); the ensemble machinery keys off "two or more direction NNs
|
 feat(ensemble): per-NN inputs replace the preset selector - the meta head becomes the vote's gate
User design (2026-08-19): 'remove the enum menu that selects neural networks... individual
inputs for every NN just like classic signals... the META NN should be integrated into the
voting decision pipeline when enabled... as a bonus meta labelling is applied to enabled NNs.'
- AI_CHOICE is GONE (tombstoned per the stale-.set doctrine). Use_MLP/Use_CONV/Use_LSTM/
Use_CONVLSTM are ordinary bools like the classic votes; the ensemble arithmetic adapts to
any subset because the consensus divisor is the enabled capable weight. Two or more
enabled = ensemble (|ENS1 token + joint gate, exactly the old AI_HYBRID fingerprints, so
existing weight files keep loading); one = the old solo preset; none = classic-only.
- Use_MetaLabeling un-couples META from the direction NNs (the old selector made them
mutually exclusive). S3 ships: CSignalMETA::LiveMetaGate scores each vote-cleared entry
(shared window at bar 1 + proposal descriptor: side, net vote, live geometry, spread/ATR;
pattern one-hot ZEROED - ranking, not calibrated probability, documented in the body) and
vetoes below the cost-adjusted break-even. Entries only; fail-open everywhere, loudly.
- COEXISTENCE HAZARDS closed: VoteCapableWeight()=0 and ProspectiveVote()=false for the
meta target - solo-only until today, a trained META would otherwise sit in the consensus
divisor as a permanent abstainer and shrink every vote by its module weight.
- CERTIFIED == TRADED: the ensemble era verdict replays the identical veto through the same
g_warriorMetaGate pointer over its OOS fired bars (bar re-resolved from the row's own
time; fail-open counted as fires and reported: 'metaGate: N approved, M vetoed, K
unscored'). The overlay deliberately does NOT replay it (veto-filter-in-replay class,
calendar-cliff precedent) - documented at the sweep site. Solo charts' own gate does not
model the veto - the standing solo-gate caveat, documented at the input.
- DB continuity: the pattern/journal DB fingerprint's first slot was (int)AIType;
DbLegacyAiSlot() maps every legacy-expressible config to its OLD value (new 2-3 member
subsets get 100+bitmask, outside the legacy range) so no existing database re-keys.
filterID becomes the enabled roster via one EnabledNNSummary().
- HUD: the meta line shows the gate (armed/(trn), last P vs BE, ok/veto tally); the
armed/disarmed announcement fires on state change via one latch (MetaGateArmedNow), not
only when an entry happens to be proposed.
NOT COMPILED - user compiles in MetaEditor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:01:02 -04:00 | | | //--- enabled" (ConfigureAISignal), which reproduces the old AI_HYBRID fingerprints exactly, and the
|
| | | //--- pattern-DB filename keeps its first slot via DbLegacyAiSlot() (Warrior_EA.mq5) so no existing
|
| | | //--- database re-keys. Deleted rather than left dangling, same doctrine as RISK_LIMIT_PCT_PRESET at
|
| | | //--- the bottom of this file: a live enum with no input behind it is exactly the stale-.set trap
|
| | | //--- shape. Stale "AIType=..." lines in saved .set files are ignored by name, harmlessly.
|
| | | //--- (Historical: value 4 was renamed AI_CONVLSTM 2026-08-15; State\HYBRID\ folder names were kept
|
| | | //--- across that rename and remain the CONVLSTM instance's identity - see CSignalHYBRID.)
|
 refactor(trade-mgmt): remove all confidence-scaled trade management
Five modes went, all of them staking real risk on the model's confidence:
Intelligent entry (ENTRY_INTELLIGENT), stop (SL_INTELLIGENT), target
(TP_INTELLIGENT), trailing (CTrailingIntelligent) and lot size
(CMoneyIntelligent's quarter-Kelly). With them, the Confidence_Source
input and the CONFIDENCE_SOURCE enum, whose only job was choosing which
number those five read.
The reason is calibration, not correctness: the confidence magnitude is
known to be miscalibrated against the label prior, so every one of these
modes multiplied money by a quantity whose units were never established.
The DB arm had a second, independent defect - since the tester DB guard
(SignalDatabaseActive) it reads 0 in tester and optimizer but non-zero
live, so any backtest of CONF_DB/CONF_BLENDED could not reproduce live
trading. And what the DB produces is a filter-RANKING win rate, not a
per-trade win probability.
Both confidence numbers are still recorded per trade (aiConfidence /
dbConfidence) and still bucketed against outcome in TradeJournalReport.
Recording is what keeps the question answerable; acting on it was the
part with no evidence behind it. ConfidenceBridge.mqh now carries an
explicit telemetry-only rule at the top.
ENUM ORDINALS PINNED. Removing a member vacated a value in four enums at
once and MT5 does not validate an enum input replayed from a saved .set
or a stored optimization pass. TRAILING_STRATEGY and
MONEY_MANAGEMENT_STRATEGY now carry explicit values so the survivors keep
the numbers they were saved as, and ValidateBarrierInputs is widened into
ValidateTradeManagementInputs covering SL_Mode, TP_Mode,
Entry_Multiplier, TrailingStrategy and MM_STRATEGY. Without that gate a
chart saved with the Intelligent stop would feed SL_Mode = -1 into a
multiplier now used verbatim, placing the stop on the wrong side of entry.
RETRAIN-NEUTRAL: neither SL_Mode nor TP_Mode appears in
BuildModelFingerprint() or ComputeDbConfigFingerprint() since the
swing-pivot target replaced the barrier labels. No .nnw, .cfg or .db
re-keys. Also drops the now-dead g_TradeRewardRiskRatio bridge, the
CMoneyRiskBase::AdjustRiskAmount hook and the unsigned AIConfidence().
Compile-verified in _claude_stage: 0 errors, 0 warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 10:10:20 -04:00 | | | //--- CONFIDENCE_SOURCE REMOVED 2026-08-25, along with the Confidence_Source input it backed and every
|
| | | //--- consumer of it. It chose which number the confidence-scaled SL/TP/entry/trail/lot modes read -
|
| | | //--- and with all five of those gone it had nothing left to steer. Two independent reasons it should
|
| | | //--- not come back in this shape:
|
| | | //--- * THE DB ARM COULD NOT SURVIVE A BACKTEST. CONF_DB / CONF_BLENDED read the signal database's
|
| | | //--- pattern win rates, and the tester DB guard (SignalDatabaseActive(), 2026-08-25) leaves that
|
| | | //--- database closed in tester and optimizer. A backtest would therefore have read 0 for a
|
| | | //--- quantity that is non-zero live - the one failure mode a backtest must not have.
|
| | | //--- * THE DB IS A WEIGHTING MECHANISM, NOT A CONFIDENCE ESTIMATE. What it produces is an average
|
| | | //--- pattern win rate used to rank filters against each other; reading it as "probability this
|
| | | //--- trade wins" was a category error that no measurement ever supported.
|
| | | //--- Both confidence numbers are still RECORDED per trade (aiConfidence / dbConfidence in
|
| | | //--- Database\TradeJournalManager.mqh, bucketed against outcome in TradeJournalReport.mqh). Recording
|
| | | //--- is how the question stays answerable; acting on it was the part that had no evidence behind it.
|
2026-08-24 18:26:25 -04:00 | | | // How many bars to wait, after a candidate ZigZag reversal bar, before trusting the real ZigZag
|
2026-07-16 00:56:33 -04:00 | | | // indicator's verdict on it as a training label - see CExpertSignalAIBase's m_swingConfirmationBars
|
| | | // declaration comment. A ZigZag's most recent 1-3 legs can still repaint as new bars arrive, so this
|
| | | // must be generous enough to let a leg fully settle (bumped from the old fractal-based system's
|
| | | // default of 20 to 100 for exactly that reason). A value of 0 is clamped up to a 1-bar minimum
|
| | | // internally, never used to mean "no delay".
|
| | | enum SWING_CONFIRMATION_PRESET
|
| | | {
|
| | | SC_10 = 10, // 10 Bars
|
| | | SC_20 = 20, // 20 Bars
|
| | | SC_30 = 30, // 30 Bars
|
| | | SC_50 = 50, // 50 Bars
|
| | | SC_100 = 100, // 100 Bars
|
| | | SC_200 = 200, // 200 Bars
|
| | | };
|
| | | enum MAX_ERAS_PRESET
|
| | | {
|
| | | ME_100 = 100, // 100 Eras
|
| | | ME_200 = 200, // 200 Eras
|
| | | ME_300 = 300, // 300 Eras
|
| | | ME_500 = 500, // 500 Eras
|
| | | ME_1000 = 1000, // 1000 Eras
|
2026-08-16 23:52:22 -04:00 | | | ME_2000 = 2000, // 2000 Eras
|
| | | ME_3000 = 3000, // 3000 Eras
|
| | | ME_5000 = 5000, // 5000 Eras
|
| | | ME_10000 = 10000, // 10000 Eras
|
2026-07-16 00:56:33 -04:00 | | | };
|
2026-07-13 03:23:39 -04:00 | | | //--- percentage of the study period held back as out-of-sample data never trained on;
|
| | | //--- value is the OOS share, in-sample share is the remainder (e.g. OOS_30 -> 70% IS / 30% OOS)
|
| | | enum OOS_SPLIT_PRESET
|
| | | {
|
| | | OOS_10 = 10, // 90% IS / 10% OOS
|
| | | OOS_20 = 20, // 80% IS / 20% OOS
|
| | | OOS_30 = 30, // 70% IS / 30% OOS
|
| | | OOS_40 = 40, // 60% IS / 40% OOS
|
| | | OOS_50 = 50, // 50% IS / 50% OOS
|
| | | };
|
2026-08-02 12:25:20 -04:00 | | | //--- RISK_LIMIT_PCT_PRESET REMOVED 2026-08-02. It backed MaxDailyLossPct/MaxDrawdownPct as a dropdown
|
| | | //--- of eight fixed percentages, which no funded-account programme is obliged to match - 4.5% or 3.75%
|
| | | //--- were unreachable. Both inputs are now free-entry doubles (Variables\Inputs.mqh) validated at init.
|
| | | //--- Note for anyone reinstating an enum input here: MT5 does NOT validate a saved enum value against
|
| | | //--- the current enum, so a .set file holding a deleted member loads as a silent out-of-range int - the
|
| | | //--- failure mode that trained four topologies on the wrong barrier (project memory: stale enum wrong
|
| | | //--- target). Changing these two to doubles removes that exposure rather than renaming it.
|
2026-07-18 17:29:38 -04:00 | | | //+------------------------------------------------------------------+
|