Warrior_EA/Expert/ADIndicatorTuner.mqh
AnimateDread b91c7b1f7a refactor(comments): box headers to stdlib length
The //| box blocks were excluded from 0b06f8e and 5efdb48 and were what
remained: 160 of them ran to 10+ lines, the longest to 88. Compressed to their
leading topic sentences - 5 lines for a function header, 8 for a file header -
keeping the box format and the standard MQL5 name/author lines verbatim.

Verified at the BYTE level this time, across every in-scope file: the list of
non-comment lines is byte-identical to HEAD and braces balance. The first check
compared a locale-decoded 'git show' against a UTF-8 read and flagged 25 files
that had not changed at all - every BOM and every non-ASCII line mismatched.

47,696 -> 40,665 lines in scope; comment share 38% -> 26%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 00:30:14 -04:00

734 lines
33 KiB
MQL5

//+------------------------------------------------------------------+
//| ADIndicatorTuner.mqh |
//| AnimateDread |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Variables\IndicatorTuneRanges.mqh"
#include "..\System\Random.mqh"
//--- flat count of AutoTuneIndicators-tunable params across all 5 AD indicators plus the
//--- MA/RSI/MACD/ Ichimoku feature periods, used to persist/restore the "winning" values in the
//--- .nnw file.
#define AD_TUNE_PARAM_COUNT 42
//+------------------------------------------------------------------+
//| Currently-active tunable input values for each AD indicator (AutoTuneIndicators search space).
//| InpContextMode/InpSessionType/InpSessionCount are session choices, not accuracy knobs, and are
//| hardcoded to the indicators' own defaults in CExpertSignalAIBase::InitAD*() rather than
//| stored/tuned here.
//+------------------------------------------------------------------+
struct SADCumulativeDeltaParams
{
int lookback;
double volClimax, volHigh, rangeClimax, rangeSignificant, stVolRatio, atrMult;
};
struct SADShorteningOfThrustParams
{
int thrustLookback, minImpulses;
double sotThreshold;
};
struct SADWyckoffEventStreamParams
{
int lookback, zigzag;
double volClimax, volHigh, rangeClimax, rangeSignificant, stVolRatio, atr;
//--- range-lifecycle knobs added by the 2026-08-02 ADWyckoffEventStream rewrite: how close price
//--- must come to a boundary to count as revisiting it (touchATR), how far the Automatic
//--- Rally/Reaction must travel off the climax before the range is confirmed and anchored
//--- (arMinATR), and the hard age cap after which a range is abandoned (maxRangeBars).
double touchATR, arMinATR;
int maxRangeBars;
};
struct SADWyckoffFailedStructureParams
{
int lookback, zigzagStrength;
double volClimax, volHigh, rangeClimax, rangeSignificant, stVolRatio, atrMult;
};
struct SADWyckoffSignificantBarInversionParams
{
int lookback;
double rangeSignificant, volumeHigh, atr;
};
//+------------------------------------------------------------------+
//| Class CADIndicatorTuner. |
//+------------------------------------------------------------------+
class CADIndicatorTuner
{
public:
SADCumulativeDeltaParams adCumDelta;
SADShorteningOfThrustParams adSOT;
SADWyckoffEventStreamParams adWES;
SADWyckoffFailedStructureParams adWFS;
SADWyckoffSignificantBarInversionParams adWSBI;
//--- AI-feature MA/RSI periods (see CExpertSignalAIBase::InitMA()/InitRSI()) - searched the same
//--- way as the AD indicators above, snapped to MA_PERIOD_PRESETS/RSI_PERIOD_PRESETS so a search
//--- never lands on a non-standard period.
int maPeriod;
//--- unified MA TYPE (MA_TYPE_PRESETS 0..8), searched alongside maPeriod when the MA feature is on.
//--- Starts from the MA_Type input; only diverges once a tuning trial runs. Feeds InitMA()'s CiCustom.
int maType;
int rsiPeriod;
//--- MACD feature periods (see CExpertSignalAIBase::InitMACDFeature()) - snapped to
//--- MACD_*_PRESETS, whose preset sets are built so every fast/slow pair stays legal no matter
//--- which one a trial perturbs (see InputEnums.mqh).
int macdFast;
int macdSlow;
int macdSignal;
//--- Ichimoku feature periods (see CExpertSignalAIBase::InitIchimoku()) - snapped to ICHIMOKU_*
//--- presets, likewise mutually-legal in any combination. Signals\SignalIchimoku.mqh is untouched.
int ichiTenkan;
int ichiKijun;
int ichiSenkou;
//--- best-known copies of the above, kept during TuneIndicatorsAndTrain() so a losing trial can
//--- restore rather than drift from a bad candidate - see SaveAsBest()/RestoreBest().
SADCumulativeDeltaParams bestAdCumDelta;
SADShorteningOfThrustParams bestAdSOT;
SADWyckoffEventStreamParams bestAdWES;
SADWyckoffFailedStructureParams bestAdWFS;
SADWyckoffSignificantBarInversionParams bestAdWSBI;
int bestMaPeriod;
int bestMaType;
int bestRsiPeriod;
int bestMacdFast;
int bestMacdSlow;
int bestMacdSignal;
int bestIchiTenkan;
int bestIchiKijun;
int bestIchiSenkou;
CADIndicatorTuner(void);
//--- flattens/restores the tunable param structs to/from a fixed-size array so they can be
//--- persisted alongside the network weights (see AI/Network.mqh Save()/Load())
void Flatten(double &arr[]);
void Unflatten(const double &arr[]);
//--- randomly perturbs one tunable param of one enabled AD indicator (or the
//--- MA/RSI/MACD/Ichimoku periods), within IndicatorTuneRanges.mqh bounds / the logical period
//--- presets. Tunable-parameter count across the ENABLED indicators only.
int ActiveDimensions(bool useCumDelta, bool useSOT, bool useWES, bool useWFS, bool useWSBI, bool useMA, bool useRSI, bool useMACD, bool useIchimoku);
//--- Candidate VALUES for one flattened parameter index (see Flatten()'s ordering), for the
//--- filter-based tuner's coordinate sweep. Discrete params return their exact preset list; continuous
//--- ones return an evenly-spaced grid across their IndicatorTuneRanges.mqh bounds. Returns the count.
int ParamCandidates(int paramIdx, double &out[]);
//--- Which indicator group owns a flattened parameter index - lets the sweep skip parameters whose
//--- indicator is switched off. 0=CumDelta 1=SOT 2=WES 3=WFS 4=WSBI 5=MA 6=RSI 7=MACD 8=Ichimoku.
int ParamOwner(int paramIdx);
void PerturbRandom(bool useCumDelta, bool useSOT, bool useWES, bool useWFS, bool useWSBI, bool useMA, bool useRSI, bool useMACD, bool useIchimoku);
//--- snapshots current -> best (a winning trial) / restores best -> current (undoing a losing trial)
void SaveAsBest(void);
void RestoreBest(void);
};
//+------------------------------------------------------------------+
//| Defaults mirror each CustomIndicators\AD*.mq5 input's own default. |
//+------------------------------------------------------------------+
CADIndicatorTuner::CADIndicatorTuner(void)
{
//--- Seeded from the AD/Wyckoff defaults (Variables\Inputs.mqh). The shared thresholds fan out
//--- to each indicator's OWN copy, so the tuner keeps full per-indicator freedom to move them
//--- apart; only the starting point is shared.
adCumDelta.lookback = ADCD_Lookback;
adCumDelta.volClimax = Wyk_VolClimaxMult;
adCumDelta.volHigh = Wyk_VolHighMult;
adCumDelta.rangeClimax = Wyk_RangeClimaxMult;
adCumDelta.rangeSignificant = Wyk_RangeSignificantMult;
adCumDelta.stVolRatio = Wyk_ShortTermVolRatio;
adCumDelta.atrMult = Wyk_AtrMult;
adSOT.thrustLookback = SOT_ThrustLookback;
adSOT.minImpulses = SOT_MinImpulses;
adSOT.sotThreshold = SOT_Threshold;
adWES.lookback = WES_Lookback;
adWES.zigzag = WES_ZigZag;
adWES.volClimax = Wyk_VolClimaxMult;
adWES.volHigh = Wyk_VolHighMult;
adWES.rangeClimax = Wyk_RangeClimaxMult;
adWES.rangeSignificant = Wyk_RangeSignificantMult;
adWES.stVolRatio = Wyk_ShortTermVolRatio;
adWES.atr = Wyk_AtrMult;
adWES.touchATR = WES_TouchATR;
adWES.arMinATR = WES_ARMinATR;
adWES.maxRangeBars = WES_MaxRangeBars;
adWFS.lookback = WFS_Lookback;
adWFS.zigzagStrength = WFS_ZigZagStrength;
adWFS.volClimax = Wyk_VolClimaxMult;
adWFS.volHigh = Wyk_VolHighMult;
adWFS.rangeClimax = Wyk_RangeClimaxMult;
adWFS.rangeSignificant = Wyk_RangeSignificantMult;
adWFS.stVolRatio = Wyk_ShortTermVolRatio;
adWFS.atrMult = Wyk_AtrMult;
adWSBI.lookback = WSBI_Lookback;
adWSBI.rangeSignificant = Wyk_RangeSignificantMult;
adWSBI.volumeHigh = Wyk_VolHighMult;
adWSBI.atr = Wyk_AtrMult;
//--- g_Tuned*, not the seed constants (2026-08-16): a chart whose earlier run INSTALLED a gated
//--- winner starts its next search from that winner, and from the same values the classic votes
//--- and DB key adopted - see Variables\TunedPeriods.mqh. On a chart never tuned, g_Tuned* ARE the
//--- seed constants and nothing changes.
maPeriod = g_TunedMaPeriod;
maType = g_TunedMaType;
rsiPeriod = g_TunedRsiPeriod;
macdFast = g_TunedMacdFast;
macdSlow = g_TunedMacdSlow;
macdSignal = g_TunedMacdSignal;
ichiTenkan = g_TunedIchiTenkan;
ichiKijun = g_TunedIchiKijun;
ichiSenkou = g_TunedIchiSenkou;
SaveAsBest();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CADIndicatorTuner::Flatten(double &arr[])
{
ArrayResize(arr, AD_TUNE_PARAM_COUNT);
int i = 0;
arr[i++] = adCumDelta.lookback;
arr[i++] = adCumDelta.volClimax;
arr[i++] = adCumDelta.volHigh;
arr[i++] = adCumDelta.rangeClimax;
arr[i++] = adCumDelta.rangeSignificant;
arr[i++] = adCumDelta.stVolRatio;
arr[i++] = adCumDelta.atrMult;
arr[i++] = adSOT.thrustLookback;
arr[i++] = adSOT.minImpulses;
arr[i++] = adSOT.sotThreshold;
arr[i++] = adWES.lookback;
arr[i++] = adWES.zigzag;
arr[i++] = adWES.volClimax;
arr[i++] = adWES.volHigh;
arr[i++] = adWES.rangeClimax;
arr[i++] = adWES.rangeSignificant;
arr[i++] = adWES.stVolRatio;
arr[i++] = adWES.atr;
arr[i++] = adWES.touchATR;
arr[i++] = adWES.arMinATR;
arr[i++] = adWES.maxRangeBars;
arr[i++] = adWFS.lookback;
arr[i++] = adWFS.zigzagStrength;
arr[i++] = adWFS.volClimax;
arr[i++] = adWFS.volHigh;
arr[i++] = adWFS.rangeClimax;
arr[i++] = adWFS.rangeSignificant;
arr[i++] = adWFS.stVolRatio;
arr[i++] = adWFS.atrMult;
arr[i++] = adWSBI.lookback;
arr[i++] = adWSBI.rangeSignificant;
arr[i++] = adWSBI.volumeHigh;
arr[i++] = adWSBI.atr;
arr[i++] = maPeriod;
arr[i++] = maType;
arr[i++] = rsiPeriod;
arr[i++] = macdFast;
arr[i++] = macdSlow;
arr[i++] = macdSignal;
arr[i++] = ichiTenkan;
arr[i++] = ichiKijun;
arr[i++] = ichiSenkou;
}
//+------------------------------------------------------------------+
//| Reverse of Flatten(); ints are rounded on the way back in since |
//| everything is carried as double in the flat array. |
//+------------------------------------------------------------------+
void CADIndicatorTuner::Unflatten(const double &arr[])
{
if(ArraySize(arr) != AD_TUNE_PARAM_COUNT)
{
// A size mismatch means AD_TUNE_PARAM_COUNT changed since this array was persisted (e.g. after
// a version upgrade) - silently keeping the constructor defaults instead of the loaded values
// used to discard a previously-tuned indicator's best parameters with zero trace in the log.
Print(__FUNCTION__ + ": persisted tuner param array size (" + IntegerToString(ArraySize(arr)) +
") does not match AD_TUNE_PARAM_COUNT (" + IntegerToString(AD_TUNE_PARAM_COUNT) +
") - discarding it and keeping constructor defaults. This model's previously-tuned indicator parameters are lost.");
return;
}
int i = 0;
adCumDelta.lookback = (int)MathRound(arr[i++]);
adCumDelta.volClimax = arr[i++];
adCumDelta.volHigh = arr[i++];
adCumDelta.rangeClimax = arr[i++];
adCumDelta.rangeSignificant = arr[i++];
adCumDelta.stVolRatio = arr[i++];
adCumDelta.atrMult = arr[i++];
adSOT.thrustLookback = (int)MathRound(arr[i++]);
adSOT.minImpulses = (int)MathRound(arr[i++]);
adSOT.sotThreshold = arr[i++];
adWES.lookback = (int)MathRound(arr[i++]);
adWES.zigzag = (int)MathRound(arr[i++]);
adWES.volClimax = arr[i++];
adWES.volHigh = arr[i++];
adWES.rangeClimax = arr[i++];
adWES.rangeSignificant = arr[i++];
adWES.stVolRatio = arr[i++];
adWES.atr = arr[i++];
adWES.touchATR = arr[i++];
adWES.arMinATR = arr[i++];
adWES.maxRangeBars = (int)MathRound(arr[i++]);
adWFS.lookback = (int)MathRound(arr[i++]);
adWFS.zigzagStrength = (int)MathRound(arr[i++]);
adWFS.volClimax = arr[i++];
adWFS.volHigh = arr[i++];
adWFS.rangeClimax = arr[i++];
adWFS.rangeSignificant = arr[i++];
adWFS.stVolRatio = arr[i++];
adWFS.atrMult = arr[i++];
adWSBI.lookback = (int)MathRound(arr[i++]);
adWSBI.rangeSignificant = arr[i++];
adWSBI.volumeHigh = arr[i++];
adWSBI.atr = arr[i++];
maPeriod = (int)MathRound(arr[i++]);
//--- a .nnw written before the built-in-MA switch cannot reach here (the type change re-keys the
//--- fingerprint, so those files load under a different name) - clamped anyway: feeding iMA a
//--- method outside ENUM_MA_METHOD yields an invalid handle and a whole dead feature column
maType = SanitizeMaType((int)MathRound(arr[i++]), false);
rsiPeriod = (int)MathRound(arr[i++]);
macdFast = (int)MathRound(arr[i++]);
macdSlow = (int)MathRound(arr[i++]);
macdSignal = (int)MathRound(arr[i++]);
ichiTenkan = (int)MathRound(arr[i++]);
ichiKijun = (int)MathRound(arr[i++]);
ichiSenkou = (int)MathRound(arr[i++]);
}
//+------------------------------------------------------------------+
//| Number of SEARCH DIMENSIONS actually in play - the count of |
//| tunable parameters across the ENABLED indicators only. |
//+------------------------------------------------------------------+
int CADIndicatorTuner::ActiveDimensions(bool useCumDelta, bool useSOT, bool useWES, bool useWFS, bool useWSBI, bool useMA, bool useRSI, bool useMACD, bool useIchimoku)
{
int d = 0;
if(useCumDelta)
d += 7; // lookback, volClimax, + 5 more - see PerturbRandom case 0
if(useSOT)
d += 3; // case 1
if(useWES)
d += 11; // case 2 - 8 thresholds + touchATR/arMinATR/maxRangeBars
if(useWFS)
d += 8; // case 3
if(useWSBI)
d += 4; // case 4
if(useMA)
d += 2; // period + type - case 5
if(useRSI)
d += 1; // period - case 6
if(useMACD)
d += 3; // fast, slow, signal - case 7
if(useIchimoku)
d += 3; // tenkan, kijun, senkou - case 8
return d;
}
//+------------------------------------------------------------------+
//| Indicator group owning a flattened parameter index. The boundaries|
//| mirror Flatten()'s write order exactly - change one, change both. |
//+------------------------------------------------------------------+
int CADIndicatorTuner::ParamOwner(int paramIdx)
{
if(paramIdx < 7) return 0; // adCumDelta: 0..6
if(paramIdx < 10) return 1; // adSOT: 7..9
if(paramIdx < 21) return 2; // adWES: 10..20
if(paramIdx < 29) return 3; // adWFS: 21..28
if(paramIdx < 33) return 4; // adWSBI: 29..32
if(paramIdx < 35) return 5; // MA: 33..34 (period, type)
if(paramIdx == 35) return 6; // RSI: 35
if(paramIdx < 39) return 7; // MACD: 36..38
return 8; // Ichimoku: 39..41
}
//+------------------------------------------------------------------+
//| Candidate values for one flattened parameter - see the header. |
//+------------------------------------------------------------------+
int CADIndicatorTuner::ParamCandidates(int paramIdx, double &out[])
{
#define TUNE_GRID_STEPS 6
//--- discrete preset lists first (identical to PerturbRandom's)
if(paramIdx == 33)
{
int v[] = {MA_PERIOD_5, MA_PERIOD_8, MA_PERIOD_9, MA_PERIOD_10, MA_PERIOD_13,
MA_PERIOD_20, MA_PERIOD_21, MA_PERIOD_50, MA_PERIOD_100, MA_PERIOD_200};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 34)
{
int v[] = {MA_TYPE_SMA, MA_TYPE_EMA, MA_TYPE_SMMA, MA_TYPE_LWMA};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 35)
{
int v[] = {RSI_PERIOD_2, RSI_PERIOD_5, RSI_PERIOD_7, RSI_PERIOD_9, RSI_PERIOD_14, RSI_PERIOD_21, RSI_PERIOD_25};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 36)
{
int v[] = {MACD_FAST_5, MACD_FAST_8, MACD_FAST_12, MACD_FAST_15};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 37)
{
int v[] = {MACD_SLOW_17, MACD_SLOW_21, MACD_SLOW_26, MACD_SLOW_34, MACD_SLOW_50};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 38)
{
int v[] = {MACD_SIGNAL_5, MACD_SIGNAL_7, MACD_SIGNAL_9, MACD_SIGNAL_12};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 39)
{
int v[] = {ICHI_TENKAN_7, ICHI_TENKAN_9, ICHI_TENKAN_12, ICHI_TENKAN_20};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 40)
{
int v[] = {ICHI_KIJUN_22, ICHI_KIJUN_26, ICHI_KIJUN_30, ICHI_KIJUN_40};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
if(paramIdx == 41)
{
int v[] = {ICHI_SENKOU_44, ICHI_SENKOU_52, ICHI_SENKOU_60, ICHI_SENKOU_120};
ArrayResize(out, ArraySize(v));
for(int i = 0; i < ArraySize(v); i++) out[i] = v[i];
return ArraySize(out);
}
//--- continuous AD parameters: even grid over the tune-range bounds. `isInt` keeps bar-count
//--- parameters whole, so a candidate is never a fractional lookback.
double lo = 0, hi = 0;
bool isInt = false;
switch(paramIdx)
{
case 0: lo = ADCUMDELTA_LOOKBACK_MIN; hi = ADCUMDELTA_LOOKBACK_MAX; isInt = true; break;
case 1: lo = ADCUMDELTA_VOLCLIMAX_MIN; hi = ADCUMDELTA_VOLCLIMAX_MAX; break;
case 2: lo = ADCUMDELTA_VOLHIGH_MIN; hi = ADCUMDELTA_VOLHIGH_MAX; break;
case 3: lo = ADCUMDELTA_RANGECLIMAX_MIN; hi = ADCUMDELTA_RANGECLIMAX_MAX; break;
case 4: lo = ADCUMDELTA_RANGESIGNIF_MIN; hi = ADCUMDELTA_RANGESIGNIF_MAX; break;
case 5: lo = ADCUMDELTA_STVOLRATIO_MIN; hi = ADCUMDELTA_STVOLRATIO_MAX; break;
case 6: lo = ADCUMDELTA_ATRMULT_MIN; hi = ADCUMDELTA_ATRMULT_MAX; break;
case 7: lo = ADSOT_LOOKBACK_MIN; hi = ADSOT_LOOKBACK_MAX; isInt = true; break;
case 8: lo = ADSOT_MININPULSES_MIN; hi = ADSOT_MININPULSES_MAX; isInt = true; break;
case 9: lo = ADSOT_THRESHOLD_MIN; hi = ADSOT_THRESHOLD_MAX; break;
case 10: lo = ADWES_LOOKBACK_MIN; hi = ADWES_LOOKBACK_MAX; isInt = true; break;
case 11: lo = ADWES_ZIGZAG_MIN; hi = ADWES_ZIGZAG_MAX; isInt = true; break;
case 12: lo = ADWES_VOLCLIMAX_MIN; hi = ADWES_VOLCLIMAX_MAX; break;
case 13: lo = ADWES_VOLHIGH_MIN; hi = ADWES_VOLHIGH_MAX; break;
case 14: lo = ADWES_RANGECLIMAX_MIN; hi = ADWES_RANGECLIMAX_MAX; break;
case 15: lo = ADWES_RANGESIGNIF_MIN; hi = ADWES_RANGESIGNIF_MAX; break;
case 16: lo = ADWES_STVOLRATIO_MIN; hi = ADWES_STVOLRATIO_MAX; break;
case 17: lo = ADWES_ATR_MIN; hi = ADWES_ATR_MAX; break;
case 18: lo = ADWES_TOUCHATR_MIN; hi = ADWES_TOUCHATR_MAX; break;
case 19: lo = ADWES_ARMINATR_MIN; hi = ADWES_ARMINATR_MAX; break;
case 20: lo = ADWES_MAXRANGEBARS_MIN; hi = ADWES_MAXRANGEBARS_MAX; isInt = true; break;
case 21: lo = ADWFS_LOOKBACK_MIN; hi = ADWFS_LOOKBACK_MAX; isInt = true; break;
case 22: lo = ADWFS_ZIGZAGSTRENGTH_MIN; hi = ADWFS_ZIGZAGSTRENGTH_MAX; isInt = true; break;
case 23: lo = ADWFS_VOLCLIMAX_MIN; hi = ADWFS_VOLCLIMAX_MAX; break;
case 24: lo = ADWFS_VOLHIGH_MIN; hi = ADWFS_VOLHIGH_MAX; break;
case 25: lo = ADWFS_RANGECLIMAX_MIN; hi = ADWFS_RANGECLIMAX_MAX; break;
case 26: lo = ADWFS_RANGESIGNIF_MIN; hi = ADWFS_RANGESIGNIF_MAX; break;
case 27: lo = ADWFS_STVOLRATIO_MIN; hi = ADWFS_STVOLRATIO_MAX; break;
case 28: lo = ADWFS_ATRMULT_MIN; hi = ADWFS_ATRMULT_MAX; break;
case 29: lo = ADWSBI_LOOKBACK_MIN; hi = ADWSBI_LOOKBACK_MAX; isInt = true; break;
case 30: lo = ADWSBI_RANGESIGNIF_MIN; hi = ADWSBI_RANGESIGNIF_MAX; break;
case 31: lo = ADWSBI_VOLHIGH_MIN; hi = ADWSBI_VOLHIGH_MAX; break;
case 32: lo = ADWSBI_ATR_MIN; hi = ADWSBI_ATR_MAX; break;
default: ArrayResize(out, 0); return 0;
}
ArrayResize(out, TUNE_GRID_STEPS);
for(int i = 0; i < TUNE_GRID_STEPS; i++)
{
double v = lo + (hi - lo) * i / (TUNE_GRID_STEPS - 1.0);
out[i] = isInt ? MathRound(v) : v;
}
return TUNE_GRID_STEPS;
}
//+------------------------------------------------------------------+
//| Randomly perturbs one tunable param of one randomly-chosen |
//| *enabled* AD indicator, within IndicatorTuneRanges.mqh bounds. |
//| No-op if no AD indicator is enabled. |
//+------------------------------------------------------------------+
void CADIndicatorTuner::PerturbRandom(bool useCumDelta, bool useSOT, bool useWES, bool useWFS, bool useWSBI, bool useMA, bool useRSI, bool useMACD, bool useIchimoku)
{
int enabled[9], n = 0;
if(useCumDelta)
enabled[n++] = 0;
if(useSOT)
enabled[n++] = 1;
if(useWES)
enabled[n++] = 2;
if(useWFS)
enabled[n++] = 3;
if(useWSBI)
enabled[n++] = 4;
if(useMA)
enabled[n++] = 5;
if(useRSI)
enabled[n++] = 6;
if(useMACD)
enabled[n++] = 7;
if(useIchimoku)
enabled[n++] = 8;
if(n == 0)
return;
switch(enabled[WarriorRandInt(n)])
{
case 0:
{
switch(WarriorRandInt(7))
{
case 0:
adCumDelta.lookback = ADCUMDELTA_LOOKBACK_MIN + WarriorRandInt(ADCUMDELTA_LOOKBACK_MAX - ADCUMDELTA_LOOKBACK_MIN + 1);
break;
case 1:
adCumDelta.volClimax = ADCUMDELTA_VOLCLIMAX_MIN + WarriorRandUniform() * (ADCUMDELTA_VOLCLIMAX_MAX - ADCUMDELTA_VOLCLIMAX_MIN);
break;
case 2:
adCumDelta.volHigh = ADCUMDELTA_VOLHIGH_MIN + WarriorRandUniform() * (ADCUMDELTA_VOLHIGH_MAX - ADCUMDELTA_VOLHIGH_MIN);
break;
case 3:
adCumDelta.rangeClimax = ADCUMDELTA_RANGECLIMAX_MIN + WarriorRandUniform() * (ADCUMDELTA_RANGECLIMAX_MAX - ADCUMDELTA_RANGECLIMAX_MIN);
break;
case 4:
adCumDelta.rangeSignificant = ADCUMDELTA_RANGESIGNIF_MIN + WarriorRandUniform() * (ADCUMDELTA_RANGESIGNIF_MAX - ADCUMDELTA_RANGESIGNIF_MIN);
break;
case 5:
adCumDelta.stVolRatio = ADCUMDELTA_STVOLRATIO_MIN + WarriorRandUniform() * (ADCUMDELTA_STVOLRATIO_MAX - ADCUMDELTA_STVOLRATIO_MIN);
break;
case 6:
adCumDelta.atrMult = ADCUMDELTA_ATRMULT_MIN + WarriorRandUniform() * (ADCUMDELTA_ATRMULT_MAX - ADCUMDELTA_ATRMULT_MIN);
break;
}
break;
}
case 1:
{
switch(WarriorRandInt(3))
{
case 0:
adSOT.thrustLookback = ADSOT_LOOKBACK_MIN + WarriorRandInt(ADSOT_LOOKBACK_MAX - ADSOT_LOOKBACK_MIN + 1);
break;
case 1:
adSOT.minImpulses = ADSOT_MININPULSES_MIN + WarriorRandInt(ADSOT_MININPULSES_MAX - ADSOT_MININPULSES_MIN + 1);
break;
case 2:
adSOT.sotThreshold = ADSOT_THRESHOLD_MIN + WarriorRandUniform() * (ADSOT_THRESHOLD_MAX - ADSOT_THRESHOLD_MIN);
break;
}
break;
}
case 2:
{
switch(WarriorRandInt(11))
{
case 0:
adWES.lookback = ADWES_LOOKBACK_MIN + WarriorRandInt(ADWES_LOOKBACK_MAX - ADWES_LOOKBACK_MIN + 1);
break;
case 1:
adWES.zigzag = ADWES_ZIGZAG_MIN + WarriorRandInt(ADWES_ZIGZAG_MAX - ADWES_ZIGZAG_MIN + 1);
break;
case 2:
adWES.volClimax = ADWES_VOLCLIMAX_MIN + WarriorRandUniform() * (ADWES_VOLCLIMAX_MAX - ADWES_VOLCLIMAX_MIN);
break;
case 3:
adWES.volHigh = ADWES_VOLHIGH_MIN + WarriorRandUniform() * (ADWES_VOLHIGH_MAX - ADWES_VOLHIGH_MIN);
break;
case 4:
adWES.rangeClimax = ADWES_RANGECLIMAX_MIN + WarriorRandUniform() * (ADWES_RANGECLIMAX_MAX - ADWES_RANGECLIMAX_MIN);
break;
case 5:
adWES.rangeSignificant = ADWES_RANGESIGNIF_MIN + WarriorRandUniform() * (ADWES_RANGESIGNIF_MAX - ADWES_RANGESIGNIF_MIN);
break;
case 6:
adWES.stVolRatio = ADWES_STVOLRATIO_MIN + WarriorRandUniform() * (ADWES_STVOLRATIO_MAX - ADWES_STVOLRATIO_MIN);
break;
case 7:
adWES.atr = ADWES_ATR_MIN + WarriorRandUniform() * (ADWES_ATR_MAX - ADWES_ATR_MIN);
break;
case 8:
adWES.touchATR = ADWES_TOUCHATR_MIN + WarriorRandUniform() * (ADWES_TOUCHATR_MAX - ADWES_TOUCHATR_MIN);
break;
case 9:
adWES.arMinATR = ADWES_ARMINATR_MIN + WarriorRandUniform() * (ADWES_ARMINATR_MAX - ADWES_ARMINATR_MIN);
break;
case 10:
adWES.maxRangeBars = ADWES_MAXRANGEBARS_MIN + WarriorRandInt(ADWES_MAXRANGEBARS_MAX - ADWES_MAXRANGEBARS_MIN + 1);
break;
}
break;
}
case 3:
{
switch(WarriorRandInt(8))
{
case 0:
adWFS.lookback = ADWFS_LOOKBACK_MIN + WarriorRandInt(ADWFS_LOOKBACK_MAX - ADWFS_LOOKBACK_MIN + 1);
break;
case 1:
adWFS.zigzagStrength = ADWFS_ZIGZAGSTRENGTH_MIN + WarriorRandInt(ADWFS_ZIGZAGSTRENGTH_MAX - ADWFS_ZIGZAGSTRENGTH_MIN + 1);
break;
case 2:
adWFS.volClimax = ADWFS_VOLCLIMAX_MIN + WarriorRandUniform() * (ADWFS_VOLCLIMAX_MAX - ADWFS_VOLCLIMAX_MIN);
break;
case 3:
adWFS.volHigh = ADWFS_VOLHIGH_MIN + WarriorRandUniform() * (ADWFS_VOLHIGH_MAX - ADWFS_VOLHIGH_MIN);
break;
case 4:
adWFS.rangeClimax = ADWFS_RANGECLIMAX_MIN + WarriorRandUniform() * (ADWFS_RANGECLIMAX_MAX - ADWFS_RANGECLIMAX_MIN);
break;
case 5:
adWFS.rangeSignificant = ADWFS_RANGESIGNIF_MIN + WarriorRandUniform() * (ADWFS_RANGESIGNIF_MAX - ADWFS_RANGESIGNIF_MIN);
break;
case 6:
adWFS.stVolRatio = ADWFS_STVOLRATIO_MIN + WarriorRandUniform() * (ADWFS_STVOLRATIO_MAX - ADWFS_STVOLRATIO_MIN);
break;
case 7:
adWFS.atrMult = ADWFS_ATRMULT_MIN + WarriorRandUniform() * (ADWFS_ATRMULT_MAX - ADWFS_ATRMULT_MIN);
break;
}
break;
}
case 4:
{
switch(WarriorRandInt(4))
{
case 0:
adWSBI.lookback = ADWSBI_LOOKBACK_MIN + WarriorRandInt(ADWSBI_LOOKBACK_MAX - ADWSBI_LOOKBACK_MIN + 1);
break;
case 1:
adWSBI.rangeSignificant = ADWSBI_RANGESIGNIF_MIN + WarriorRandUniform() * (ADWSBI_RANGESIGNIF_MAX - ADWSBI_RANGESIGNIF_MIN);
break;
case 2:
adWSBI.volumeHigh = ADWSBI_VOLHIGH_MIN + WarriorRandUniform() * (ADWSBI_VOLHIGH_MAX - ADWSBI_VOLHIGH_MIN);
break;
case 3:
adWSBI.atr = ADWSBI_ATR_MIN + WarriorRandUniform() * (ADWSBI_ATR_MAX - ADWSBI_ATR_MIN);
break;
}
break;
}
case 5:
{
//--- the MA feature has TWO tunables now (period + type); pick one at random to perturb
if(WarriorRandInt(2) == 0)
{
//--- snapped to MA_PERIOD_PRESETS (InputEnums.mqh) so a search never lands on a non-standard period
int maChoices[] = {MA_PERIOD_5, MA_PERIOD_8, MA_PERIOD_9, MA_PERIOD_10, MA_PERIOD_13, MA_PERIOD_20, MA_PERIOD_21, MA_PERIOD_50, MA_PERIOD_100, MA_PERIOD_200};
maPeriod = maChoices[WarriorRandInt(ArraySize(maChoices))];
}
else
{
//--- every MA_TYPE_PRESETS value, i.e. every ENUM_MA_METHOD iMA accepts
int maTypeChoices[] = {MA_TYPE_SMA, MA_TYPE_EMA, MA_TYPE_SMMA, MA_TYPE_LWMA};
maType = maTypeChoices[WarriorRandInt(ArraySize(maTypeChoices))];
}
break;
}
case 6:
{
//--- snapped to RSI_PERIOD_PRESETS (InputEnums.mqh)
int rsiChoices[] = {RSI_PERIOD_2, RSI_PERIOD_5, RSI_PERIOD_7, RSI_PERIOD_9, RSI_PERIOD_14, RSI_PERIOD_21, RSI_PERIOD_25};
rsiPeriod = rsiChoices[WarriorRandInt(ArraySize(rsiChoices))];
break;
}
case 7:
{
//--- the MACD feature has THREE tunables (fast/slow/signal); pick one at random. No cross-check
//--- against the others is needed - the MACD_*_PRESETS sets never overlap, so any fast is always
//--- below any slow (see InputEnums.mqh).
switch(WarriorRandInt(3))
{
case 0:
{
int fastChoices[] = {MACD_FAST_5, MACD_FAST_8, MACD_FAST_12, MACD_FAST_15};
macdFast = fastChoices[WarriorRandInt(ArraySize(fastChoices))];
break;
}
case 1:
{
int slowChoices[] = {MACD_SLOW_17, MACD_SLOW_21, MACD_SLOW_26, MACD_SLOW_34, MACD_SLOW_50};
macdSlow = slowChoices[WarriorRandInt(ArraySize(slowChoices))];
break;
}
case 2:
{
int signalChoices[] = {MACD_SIGNAL_5, MACD_SIGNAL_7, MACD_SIGNAL_9, MACD_SIGNAL_12};
macdSignal = signalChoices[WarriorRandInt(ArraySize(signalChoices))];
break;
}
}
break;
}
case 8:
{
//--- the Ichimoku feature has THREE tunables (Tenkan/Kijun/Senkou B); pick one at random. Same
//--- non-overlapping-presets guarantee as MACD above keeps Tenkan < Kijun < Senkou B holding
//--- whichever single one this trial moves.
switch(WarriorRandInt(3))
{
case 0:
{
int tenkanChoices[] = {ICHI_TENKAN_7, ICHI_TENKAN_9, ICHI_TENKAN_12, ICHI_TENKAN_20};
ichiTenkan = tenkanChoices[WarriorRandInt(ArraySize(tenkanChoices))];
break;
}
case 1:
{
int kijunChoices[] = {ICHI_KIJUN_22, ICHI_KIJUN_26, ICHI_KIJUN_30, ICHI_KIJUN_40};
ichiKijun = kijunChoices[WarriorRandInt(ArraySize(kijunChoices))];
break;
}
case 2:
{
int senkouChoices[] = {ICHI_SENKOU_44, ICHI_SENKOU_52, ICHI_SENKOU_60, ICHI_SENKOU_120};
ichiSenkou = senkouChoices[WarriorRandInt(ArraySize(senkouChoices))];
break;
}
}
break;
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CADIndicatorTuner::SaveAsBest(void)
{
bestAdCumDelta = adCumDelta;
bestAdSOT = adSOT;
bestAdWES = adWES;
bestAdWFS = adWFS;
bestAdWSBI = adWSBI;
bestMaPeriod = maPeriod;
bestMaType = maType;
bestRsiPeriod = rsiPeriod;
bestMacdFast = macdFast;
bestMacdSlow = macdSlow;
bestMacdSignal = macdSignal;
bestIchiTenkan = ichiTenkan;
bestIchiKijun = ichiKijun;
bestIchiSenkou = ichiSenkou;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CADIndicatorTuner::RestoreBest(void)
{
adCumDelta = bestAdCumDelta;
adSOT = bestAdSOT;
adWES = bestAdWES;
adWFS = bestAdWFS;
adWSBI = bestAdWSBI;
maPeriod = bestMaPeriod;
maType = bestMaType;
rsiPeriod = bestRsiPeriod;
macdFast = bestMacdFast;
macdSlow = bestMacdSlow;
macdSignal = bestMacdSignal;
ichiTenkan = bestIchiTenkan;
ichiKijun = bestIchiKijun;
ichiSenkou = bestIchiSenkou;
}
//+------------------------------------------------------------------+