forked from animatedread/Warrior_EA
CExpertSignalAIBase (4,326 lines, one class) carried 5 struct definitions, 10 member fields, and 3 methods (Flatten/Unflatten/ PerturbRandom) purely for the AutoTuneIndicators search-space state - entirely self-contained (never touches Net, Train()'s resumable state machine, or anything else in the class). Extracted into a new Expert/ADIndicatorTuner.mqh (CADIndicatorTuner), held as a single m_indicatorTuner member. TuneIndicatorsAndTrain() itself - the outer loop that actually orchestrates Train()/Net/checkpointing around this tuner - turned out to be exactly as tightly coupled to Train()'s resumable state machine as Train() itself, so per the same caution already applied to Train() in this refactor pass, it stays in CExpertSignalAIBase rather than being pulled into the collaborator; it now calls the tuner's public Flatten()/Unflatten()/PerturbRandom()/SaveAsBest()/RestoreBest() instead of manipulating the structs inline. All internal field-access renames (m_adCumDeltaParams.lookback -> m_indicatorTuner.adCumDelta.lookback, etc., ~40 sites across the 5 InitAD*() indicator-setup methods) verified against a full grep sweep - no leftover references to the old field/method names. Compiled clean (MetaEditor, 0 errors/0 warnings). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
370 lines
16 KiB
MQL5
370 lines
16 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ADIndicatorTuner.mqh |
|
|
//| AnimateDread |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#include "..\Variables\IndicatorTuneRanges.mqh"
|
|
//--- flat count of AutoTuneIndicators-tunable params across all 5 AD indicators, used to
|
|
//--- persist/restore the "winning" values in the .nnw file. ATR and Volume are intentionally NOT
|
|
//--- included: ATR's only tunable knob is m_periods, the shared bar lookback (ind_Periods);
|
|
//--- Volume's applied-price type (tick vs real) is kept as a plain manual input (VolumeData) since
|
|
//--- not every symbol has real volume available.
|
|
#define AD_TUNE_PARAM_COUNT 30
|
|
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
};
|
|
struct SADWyckoffFailedStructureParams
|
|
{
|
|
int lookback, zigzagStrength;
|
|
double volClimax, volHigh, rangeClimax, rangeSignificant, stVolRatio, atrMult;
|
|
};
|
|
struct SADWyckoffSignificantBarInversionParams
|
|
{
|
|
int lookback;
|
|
double rangeSignificant, volumeHigh, atr;
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Class CADIndicatorTuner. |
|
|
//| Owns the AutoTuneIndicators search-space state (current + best-known tunable values for all 5 |
|
|
//| AD indicators) and its own mutation logic (random perturbation, flatten/unflatten for .nnw |
|
|
//| persistence, win/loss bookkeeping) - extracted out of CExpertSignalAIBase (SOLID cleanup) since |
|
|
//| this state and behavior is entirely self-contained: it never touches Net, Train()'s state |
|
|
//| machine, or anything else in CExpertSignalAIBase. CExpertSignalAIBase::TuneIndicatorsAndTrain() |
|
|
//| (which DOES orchestrate Train()/Net/checkpointing around this tuner) stays put - that outer loop |
|
|
//| is exactly as tightly coupled to Train()'s resumable state machine as Train() itself, so it's |
|
|
//| deliberately NOT pulled in here; see this file's declaration comment in ExpertSignalAIBase.mqh |
|
|
//| for the full rationale. |
|
|
//+------------------------------------------------------------------+
|
|
class CADIndicatorTuner
|
|
{
|
|
public:
|
|
SADCumulativeDeltaParams adCumDelta;
|
|
SADShorteningOfThrustParams adSOT;
|
|
SADWyckoffEventStreamParams adWES;
|
|
SADWyckoffFailedStructureParams adWFS;
|
|
SADWyckoffSignificantBarInversionParams adWSBI;
|
|
//--- 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;
|
|
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, within IndicatorTuneRanges.mqh
|
|
//--- bounds. useCumDelta/useSOT/useWES/useWFS/useWSBI mirror CExpertSignalAIBase's
|
|
//--- m_useADCumulativeDelta/etc. enable flags - no-op if all five are false.
|
|
void PerturbRandom(bool useCumDelta, bool useSOT, bool useWES, bool useWFS, bool useWSBI);
|
|
//--- 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)
|
|
{
|
|
adCumDelta.lookback = 50;
|
|
adCumDelta.volClimax = 2.5;
|
|
adCumDelta.volHigh = 1.5;
|
|
adCumDelta.rangeClimax = 1.8;
|
|
adCumDelta.rangeSignificant = 1.2;
|
|
adCumDelta.stVolRatio = 0.6;
|
|
adCumDelta.atrMult = 0.5;
|
|
adSOT.thrustLookback = 30;
|
|
adSOT.minImpulses = 3;
|
|
adSOT.sotThreshold = 0.30;
|
|
adWES.lookback = 50;
|
|
adWES.zigzag = 3;
|
|
adWES.volClimax = 2.5;
|
|
adWES.volHigh = 1.5;
|
|
adWES.rangeClimax = 1.8;
|
|
adWES.rangeSignificant = 1.2;
|
|
adWES.stVolRatio = 0.6;
|
|
adWES.atr = 0.5;
|
|
adWFS.lookback = 50;
|
|
adWFS.zigzagStrength = 3;
|
|
adWFS.volClimax = 2.5;
|
|
adWFS.volHigh = 1.5;
|
|
adWFS.rangeClimax = 1.8;
|
|
adWFS.rangeSignificant = 1.2;
|
|
adWFS.stVolRatio = 0.6;
|
|
adWFS.atrMult = 0.5;
|
|
adWSBI.lookback = 50;
|
|
adWSBI.rangeSignificant = 1.2;
|
|
adWSBI.volumeHigh = 1.5;
|
|
adWSBI.atr = 0.5;
|
|
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++] = 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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)
|
|
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++];
|
|
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++];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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)
|
|
{
|
|
int enabled[5], 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(n == 0)
|
|
return;
|
|
switch(enabled[MathRand() % n])
|
|
{
|
|
case 0:
|
|
{
|
|
switch(MathRand() % 7)
|
|
{
|
|
case 0:
|
|
adCumDelta.lookback = ADCUMDELTA_LOOKBACK_MIN + MathRand() % (ADCUMDELTA_LOOKBACK_MAX - ADCUMDELTA_LOOKBACK_MIN + 1);
|
|
break;
|
|
case 1:
|
|
adCumDelta.volClimax = ADCUMDELTA_VOLCLIMAX_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_VOLCLIMAX_MAX - ADCUMDELTA_VOLCLIMAX_MIN);
|
|
break;
|
|
case 2:
|
|
adCumDelta.volHigh = ADCUMDELTA_VOLHIGH_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_VOLHIGH_MAX - ADCUMDELTA_VOLHIGH_MIN);
|
|
break;
|
|
case 3:
|
|
adCumDelta.rangeClimax = ADCUMDELTA_RANGECLIMAX_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_RANGECLIMAX_MAX - ADCUMDELTA_RANGECLIMAX_MIN);
|
|
break;
|
|
case 4:
|
|
adCumDelta.rangeSignificant = ADCUMDELTA_RANGESIGNIF_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_RANGESIGNIF_MAX - ADCUMDELTA_RANGESIGNIF_MIN);
|
|
break;
|
|
case 5:
|
|
adCumDelta.stVolRatio = ADCUMDELTA_STVOLRATIO_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_STVOLRATIO_MAX - ADCUMDELTA_STVOLRATIO_MIN);
|
|
break;
|
|
case 6:
|
|
adCumDelta.atrMult = ADCUMDELTA_ATRMULT_MIN + (MathRand() / 32767.0) * (ADCUMDELTA_ATRMULT_MAX - ADCUMDELTA_ATRMULT_MIN);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
switch(MathRand() % 3)
|
|
{
|
|
case 0:
|
|
adSOT.thrustLookback = ADSOT_LOOKBACK_MIN + MathRand() % (ADSOT_LOOKBACK_MAX - ADSOT_LOOKBACK_MIN + 1);
|
|
break;
|
|
case 1:
|
|
adSOT.minImpulses = ADSOT_MININPULSES_MIN + MathRand() % (ADSOT_MININPULSES_MAX - ADSOT_MININPULSES_MIN + 1);
|
|
break;
|
|
case 2:
|
|
adSOT.sotThreshold = ADSOT_THRESHOLD_MIN + (MathRand() / 32767.0) * (ADSOT_THRESHOLD_MAX - ADSOT_THRESHOLD_MIN);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
switch(MathRand() % 8)
|
|
{
|
|
case 0:
|
|
adWES.lookback = ADWES_LOOKBACK_MIN + MathRand() % (ADWES_LOOKBACK_MAX - ADWES_LOOKBACK_MIN + 1);
|
|
break;
|
|
case 1:
|
|
adWES.zigzag = ADWES_ZIGZAG_MIN + MathRand() % (ADWES_ZIGZAG_MAX - ADWES_ZIGZAG_MIN + 1);
|
|
break;
|
|
case 2:
|
|
adWES.volClimax = ADWES_VOLCLIMAX_MIN + (MathRand() / 32767.0) * (ADWES_VOLCLIMAX_MAX - ADWES_VOLCLIMAX_MIN);
|
|
break;
|
|
case 3:
|
|
adWES.volHigh = ADWES_VOLHIGH_MIN + (MathRand() / 32767.0) * (ADWES_VOLHIGH_MAX - ADWES_VOLHIGH_MIN);
|
|
break;
|
|
case 4:
|
|
adWES.rangeClimax = ADWES_RANGECLIMAX_MIN + (MathRand() / 32767.0) * (ADWES_RANGECLIMAX_MAX - ADWES_RANGECLIMAX_MIN);
|
|
break;
|
|
case 5:
|
|
adWES.rangeSignificant = ADWES_RANGESIGNIF_MIN + (MathRand() / 32767.0) * (ADWES_RANGESIGNIF_MAX - ADWES_RANGESIGNIF_MIN);
|
|
break;
|
|
case 6:
|
|
adWES.stVolRatio = ADWES_STVOLRATIO_MIN + (MathRand() / 32767.0) * (ADWES_STVOLRATIO_MAX - ADWES_STVOLRATIO_MIN);
|
|
break;
|
|
case 7:
|
|
adWES.atr = ADWES_ATR_MIN + (MathRand() / 32767.0) * (ADWES_ATR_MAX - ADWES_ATR_MIN);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
switch(MathRand() % 8)
|
|
{
|
|
case 0:
|
|
adWFS.lookback = ADWFS_LOOKBACK_MIN + MathRand() % (ADWFS_LOOKBACK_MAX - ADWFS_LOOKBACK_MIN + 1);
|
|
break;
|
|
case 1:
|
|
adWFS.zigzagStrength = ADWFS_ZIGZAGSTRENGTH_MIN + MathRand() % (ADWFS_ZIGZAGSTRENGTH_MAX - ADWFS_ZIGZAGSTRENGTH_MIN + 1);
|
|
break;
|
|
case 2:
|
|
adWFS.volClimax = ADWFS_VOLCLIMAX_MIN + (MathRand() / 32767.0) * (ADWFS_VOLCLIMAX_MAX - ADWFS_VOLCLIMAX_MIN);
|
|
break;
|
|
case 3:
|
|
adWFS.volHigh = ADWFS_VOLHIGH_MIN + (MathRand() / 32767.0) * (ADWFS_VOLHIGH_MAX - ADWFS_VOLHIGH_MIN);
|
|
break;
|
|
case 4:
|
|
adWFS.rangeClimax = ADWFS_RANGECLIMAX_MIN + (MathRand() / 32767.0) * (ADWFS_RANGECLIMAX_MAX - ADWFS_RANGECLIMAX_MIN);
|
|
break;
|
|
case 5:
|
|
adWFS.rangeSignificant = ADWFS_RANGESIGNIF_MIN + (MathRand() / 32767.0) * (ADWFS_RANGESIGNIF_MAX - ADWFS_RANGESIGNIF_MIN);
|
|
break;
|
|
case 6:
|
|
adWFS.stVolRatio = ADWFS_STVOLRATIO_MIN + (MathRand() / 32767.0) * (ADWFS_STVOLRATIO_MAX - ADWFS_STVOLRATIO_MIN);
|
|
break;
|
|
case 7:
|
|
adWFS.atrMult = ADWFS_ATRMULT_MIN + (MathRand() / 32767.0) * (ADWFS_ATRMULT_MAX - ADWFS_ATRMULT_MIN);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
switch(MathRand() % 4)
|
|
{
|
|
case 0:
|
|
adWSBI.lookback = ADWSBI_LOOKBACK_MIN + MathRand() % (ADWSBI_LOOKBACK_MAX - ADWSBI_LOOKBACK_MIN + 1);
|
|
break;
|
|
case 1:
|
|
adWSBI.rangeSignificant = ADWSBI_RANGESIGNIF_MIN + (MathRand() / 32767.0) * (ADWSBI_RANGESIGNIF_MAX - ADWSBI_RANGESIGNIF_MIN);
|
|
break;
|
|
case 2:
|
|
adWSBI.volumeHigh = ADWSBI_VOLHIGH_MIN + (MathRand() / 32767.0) * (ADWSBI_VOLHIGH_MAX - ADWSBI_VOLHIGH_MIN);
|
|
break;
|
|
case 3:
|
|
adWSBI.atr = ADWSBI_ATR_MIN + (MathRand() / 32767.0) * (ADWSBI_ATR_MAX - ADWSBI_ATR_MIN);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CADIndicatorTuner::SaveAsBest(void)
|
|
{
|
|
bestAdCumDelta = adCumDelta;
|
|
bestAdSOT = adSOT;
|
|
bestAdWES = adWES;
|
|
bestAdWFS = adWFS;
|
|
bestAdWSBI = adWSBI;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CADIndicatorTuner::RestoreBest(void)
|
|
{
|
|
adCumDelta = bestAdCumDelta;
|
|
adSOT = bestAdSOT;
|
|
adWES = bestAdWES;
|
|
adWFS = bestAdWFS;
|
|
adWSBI = bestAdWSBI;
|
|
}
|
|
//+------------------------------------------------------------------+
|