- Replaced standard library signal modules with custom implementations to allow for named patterns and improved voting. - Added new input parameters for module weights, allowing for optimization of individual signal contributions. - Enhanced the management of trades with new options for breakeven and management cut. - Introduced a mechanism for dynamic ranking of signal weights based on historical performance. - Improved initialization logic to ensure proper registration of filters and handling of trading conditions. - Added detailed logging for trading permissions and account status during initialization.
209 lines
9.7 KiB
MQL5
209 lines
9.7 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| WarriorEnums.mqh |
|
|
//| AnimateDread |
|
|
//| |
|
|
//| EVERY INPUT THIS EA TAKES IS AN ENUM, and each enum's VALUE IS |
|
|
//| ITS PAYLOAD. SL_ATR_x2 is literally 2, RISK_PCT_3 is literally 3, |
|
|
//| so a consumer writes `m_atr * SL_Mode` with no lookup table and |
|
|
//| no switch that can fall out of step with the list. The MT5 |
|
|
//| optimizer steps over members, so a sweep can only ever produce a |
|
|
//| legal configuration - which is the real reason for the dropdowns. |
|
|
//| |
|
|
//| ORDINALS ARE PINNED AND NEVER REUSED. MT5 stores an input's VALUE |
|
|
//| in the .set file and in each chart's .chr, and does not validate |
|
|
//| it against the current enum. Renumbering a member therefore |
|
|
//| silently re-points every saved configuration at a different |
|
|
//| setting. Gaps in the sequence are deliberate headstones for |
|
|
//| members that were removed; leave them. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_SIMPLE_ENUMS_MQH
|
|
#define WARRIOR_SIMPLE_ENUMS_MQH
|
|
|
|
//--- Which way this chart is allowed to trade. LONG_ONLY exists because an index is not symmetric;
|
|
//--- it is a legitimate setting, not a crutch. Value 1 was the default a stale tester preset once
|
|
//--- supplied to every run, turning a two-sided EA long-only for days, so read the init echo.
|
|
enum WARRIOR_DIRECTION
|
|
{
|
|
DIR_BOTH = 0, // Both directions
|
|
DIR_LONG = 1, // Long only
|
|
DIR_SHORT = 2, // Short only
|
|
};
|
|
|
|
//--- Position sizing. 1 is deliberately vacant: it was INTELLIGENT, which staked real money on an
|
|
//--- uncalibrated confidence number (and in an earlier form increased size after a losing streak).
|
|
//--- It is not coming back, and the hole stops a saved "1" from selecting something else.
|
|
enum WARRIOR_MONEY_MODE
|
|
{
|
|
MONEY_FIXED_RISK = 0, // Risk a fixed % of balance
|
|
MONEY_FIXED_LOT = 2, // Fixed lot size
|
|
};
|
|
|
|
//--- Risk per trade, as a whole percent of balance. The value IS the percent.
|
|
enum WARRIOR_RISK_PCT
|
|
{
|
|
RISK_1 = 1, // 1%
|
|
RISK_2 = 2, // 2%
|
|
RISK_3 = 3, // 3%
|
|
RISK_5 = 5, // 5%
|
|
};
|
|
|
|
//--- The stop, in ATR from entry. The value IS the multiple.
|
|
enum WARRIOR_SL_MODE
|
|
{
|
|
SL_ATR_x1 = 1, // ATR x 1
|
|
SL_ATR_x2 = 2, // ATR x 2
|
|
SL_ATR_x3 = 3, // ATR x 3
|
|
SL_ATR_x4 = 4, // ATR x 4
|
|
};
|
|
|
|
//--- The target, in ATR from entry. 0 = none, run it on the trail alone.
|
|
enum WARRIOR_TP_MODE
|
|
{
|
|
TP_NONE = 0, // No target (trail only)
|
|
TP_ATR_x1 = 1, // ATR x 1
|
|
TP_ATR_x2 = 2, // ATR x 2
|
|
TP_ATR_x3 = 3, // ATR x 3
|
|
TP_ATR_x4 = 4, // ATR x 4
|
|
TP_ATR_x6 = 6, // ATR x 6
|
|
TP_ATR_x8 = 8, // ATR x 8
|
|
TP_ATR_x10 = 10, // ATR x 10
|
|
};
|
|
|
|
//--- The trailing stop, in ATR behind price. The value IS the multiple; 0 disables it.
|
|
enum WARRIOR_TRAIL_MODE
|
|
{
|
|
TRAIL_NONE = 0, // No trailing stop
|
|
TRAIL_ATR_x1 = 1, // Trail at ATR x 1
|
|
TRAIL_ATR_x2 = 2, // Trail at ATR x 2
|
|
TRAIL_ATR_x3 = 3, // Trail at ATR x 3
|
|
};
|
|
|
|
//--- THE VOTE THRESHOLD, as a percent of the ensemble's weight.
|
|
//---
|
|
//--- ⚠ WHAT THIS MEANS DEPENDS ON THE ROSTER, and that is the single easiest way to misread this EA.
|
|
//--- CExpertSignal::Direction() divides the weighted sum by the NUMBER OF FILTERS THAT ANSWERED, so
|
|
//--- one unopposed voter at full weight scores 100/N. With 19 filters registered that is 5.3%, and a
|
|
//--- threshold of 15% therefore demands three agreeing voters, not one. Change the roster and every
|
|
//--- rung on this list means something different. The EA prints the resolved ladder at init for
|
|
//--- exactly this reason - read that, not this comment.
|
|
//---
|
|
//--- The sub-5% rungs exist because a large roster pushes the whole useful range down there.
|
|
enum WARRIOR_THRESHOLD
|
|
{
|
|
THR_1 = 1, // 1%
|
|
THR_2 = 2, // 2%
|
|
THR_3 = 3, // 3%
|
|
THR_4 = 4, // 4%
|
|
THR_5 = 5, // 5%
|
|
THR_10 = 10, // 10%
|
|
THR_15 = 15, // 15%
|
|
THR_20 = 20, // 20%
|
|
THR_25 = 25, // 25%
|
|
THR_30 = 30, // 30%
|
|
THR_40 = 40, // 40%
|
|
THR_50 = 50, // 50%
|
|
};
|
|
|
|
//--- THE PROBABILITY CUT for anything that outputs a calibrated probability - the neural module.
|
|
//--- Stored as integer percent, read as value/100.0.
|
|
//---
|
|
//--- 50 IS "NO CUT" AND IS THE RIGHT DEFAULT until a trained model's calibration has been measured
|
|
//--- on the instrument it will trade. A threshold chosen before a calibration curve exists is a
|
|
//--- guess wearing a setting's clothes, and it will look like a decision in the .set file forever.
|
|
//---
|
|
//--- ⚠ FOR THE FRACTAL NET, EXPECT THE USEFUL CUT TO BE LOW, and to act as a floor rather than a
|
|
//--- trigger: measured mean R by predicted-probability quintile was -0.230 / -0.089 / -0.026 /
|
|
//--- +0.002 / +0.017 across 20 series. Nearly all of the separation is in refusing the bottom, not
|
|
//--- in selecting the top - so raising this hunting for a "high conviction" band is chasing a part
|
|
//--- of the curve that was measured flat.
|
|
enum WARRIOR_CONFIDENCE
|
|
{
|
|
CONF_50 = 50, // 0.50 - no cut
|
|
CONF_55 = 55, // 0.55
|
|
CONF_60 = 60, // 0.60
|
|
CONF_65 = 65, // 0.65
|
|
CONF_70 = 70, // 0.70
|
|
CONF_75 = 75, // 0.75
|
|
CONF_80 = 80, // 0.80
|
|
CONF_90 = 90, // 0.90
|
|
};
|
|
//--- A MODULE'S WEIGHT IN THE VOTE, as a percent. The value IS the percent, so WEIGHT_100 is 1.0.
|
|
//---
|
|
//--- These are SWEPT BY THE OPTIMISER, alongside the threshold and the ATR multiples, and that is
|
|
//--- the deliberate division of labour with the database: the journal sets the 49 PATTERN weights
|
|
//--- from measured expectancy (far too many to optimise), the optimiser sets the 12 MODULE weights
|
|
//--- (few enough to be tractable, and coarse enough that a genetic pass can explore them).
|
|
//---
|
|
//--- WEIGHT_0 IS THE USEFUL ONE. A module at zero is switched off without being deleted, so the
|
|
//--- optimiser can discover that an indicator contributes nothing - which is the question this EA
|
|
//--- has never been able to ask, because every module has always voted at weight 1.0 by default.
|
|
enum WARRIOR_WEIGHT
|
|
{
|
|
WEIGHT_0 = 0, // 0.0 - silent
|
|
WEIGHT_25 = 25, // 0.25
|
|
WEIGHT_50 = 50, // 0.50
|
|
WEIGHT_75 = 75, // 0.75
|
|
WEIGHT_100 = 100, // 1.00 - the default
|
|
WEIGHT_150 = 150, // 1.50
|
|
WEIGHT_200 = 200, // 2.00
|
|
};
|
|
//--- MOVE THE STOP TO ENTRY once the trade is this many TENTHS of R in front. The value IS the
|
|
//--- tenths, so BE_AT_0R5 is 5 and the consumer divides by 10.
|
|
//---
|
|
//--- ⚠ THIS IS NOT A TRAILING STOP AND MUST NOT BE CONFUSED WITH ONE. Measured 2026-09-11: every
|
|
//--- ATR-trail setting made results WORSE (+150.67 -> +90.56 at 1 ATR, worse as it widened),
|
|
//--- because a trail keeps moving and exits the winner on its first pullback. Breakeven moves
|
|
//--- ONCE and then stands still, so it scratches the loser without capping the winner. On 5,491
|
|
//--- traded-side firings the counterfactual was mean R -0.132 -> +0.216 at +0.5R.
|
|
enum WARRIOR_BREAKEVEN
|
|
{
|
|
BE_OFF = 0, // Never move the stop to entry
|
|
BE_AT_0R5 = 5, // +0.5 R
|
|
BE_AT_0R7 = 7, // +0.7 R
|
|
BE_AT_1R0 = 10, // +1.0 R
|
|
BE_AT_1R5 = 15, // +1.5 R
|
|
};
|
|
//--- MANAGEMENT MODEL CUT. At +0.5R the model is asked "does this continue?"; below this
|
|
//--- probability the trade is closed at the crossing instead of being left to the stop. The value
|
|
//--- IS the percent. MGMT_OFF disables both the model and its training walk.
|
|
//---
|
|
//--- ⚠ 46% of crossings continue and 54% retrace, so a cut of 50 acts on almost every trade while
|
|
//--- a cut of 30 acts only where the model is confident. Which is right depends on the model's
|
|
//--- calibration, which is why this is an input and not a constant - and why the net prints its
|
|
//--- held-out AUC: a cut applied to a coin-flip model is just a random early exit.
|
|
enum WARRIOR_MGMT_CUT
|
|
{
|
|
MGMT_OFF = 0, // No management model
|
|
MGMT_CUT30 = 30, // exit when P(continue) < 0.30
|
|
MGMT_CUT40 = 40, // < 0.40
|
|
MGMT_CUT50 = 50, // < 0.50
|
|
MGMT_CUT60 = 60, // < 0.60
|
|
};
|
|
//--- FADE THE CROWD. When at least this many filters vote the SAME way, take the OTHER side; when
|
|
//--- fewer do, stand aside entirely. The value IS the voter count.
|
|
//---
|
|
//--- WHY THIS EXISTS. Measured on 32,409 traded bar-sides (4 majors, H4, 2015-2026) the ensemble's
|
|
//--- conviction is monotonically ANTI-predictive: agree=12 -0.27R, agree=13 -0.61R, in both halves,
|
|
//--- on every symbol. Priced exactly from the journal as a symmetric-1R fade (wins iff the traded
|
|
//--- side touched -1R before +1R, read from mfeR, ties scored AGAINST the fade): agree>=12 gives
|
|
//--- +0.294R in-sample and +0.323R out of sample, every symbol, every t >= 3.5. The mechanism is
|
|
//--- plain: these are lagging indicators, the vote fires after the move, and when ALL of them agree
|
|
//--- the move is mature. Unanimity is exhaustion.
|
|
//--- USE WITH StopMode == TargetMode (1R/1R). The measurement was symmetric; an asymmetric fade is
|
|
//--- a different trade that has not been priced.
|
|
enum WARRIOR_FADE
|
|
{
|
|
FADE_OFF = 0, // Normal voting
|
|
FADE_AGREE_11 = 11, // Fade when >= 11 filters agree (+0.15R measured)
|
|
FADE_AGREE_12 = 12, // Fade when >= 12 agree (+0.29R measured)
|
|
FADE_AGREE_13 = 13, // Fade only unanimity (+0.6R measured, rare)
|
|
};
|
|
//--- THE DIP-BUY'S TWO MEASURED FORMS (Signals\SignalDipBuy.mqh has the numbers): the daily RSI(2)
|
|
//--- Connors form, and the z-score form that the seven-style scan found to be the deepest and the
|
|
//--- only one with H4 cadence (~15 trades a month on SP500).
|
|
enum WARRIOR_DIP_ENTRY
|
|
{
|
|
DIP_RSI2 = 0, // RSI(2) at or below RsiEntry (the daily Connors form)
|
|
DIP_ZSCORE = 1, // close below the 20-bar mean by DipZ standard deviations
|
|
};
|
|
#endif // WARRIOR_SIMPLE_ENUMS_MQH
|