Warrior_EA/Expert/AIBase/Features.mqh
AnimateDread 26ac479bae docs: refactor + audit notes; fix malformed comment banner
REFACTOR_NOTES.md records what was found, what was changed, what was
deliberately left alone, and the one investigation that is still open (the
MLP CPU-DLL slowdown, with the parameter counts that rule out my earlier
"largest weight matrix" explanation).

Also restores the missing opening rule on ReInitADIndicators' comment banner.

Compiles 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 00:46:01 -04:00

1112 lines
55 KiB
MQL5

//+------------------------------------------------------------------+
//| Warrior_EA |
//| AnimateDread |
//| |
//| Indicator creation and the per-bar input feature vector. |
//| |
//| PARTIAL IMPLEMENTATION FILE - not standalone. |
//| This holds CExpertSignalAIBase method BODIES only. The class |
//| declaration lives in Expert\ExpertSignalAIBase.mqh, which |
//| #includes this file at the bottom, after the declaration. Do not |
//| include it anywhere else and do not compile it on its own. |
//| |
//| Split out purely to make the 8216-line original navigable; the |
//| code inside was moved verbatim, not rewritten. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_AIBASE_FEATURES_MQH
#define WARRIOR_AIBASE_FEATURES_MQH
//+------------------------------------------------------------------+
//| Rebuilds only the enabled AD* CiCustom handles in place, so a new |
//| trial's member-struct param values take effect. Re-Create()-ing |
//| the existing CiCustom object (rather than removing/re-adding it |
//| to indicators) avoids adding the same pointer into the CIndicators|
//| collection twice, which would risk it being deleted twice on |
//| teardown - MQL5's CIndicators has no documented single-item |
//| remove, and CiCustom.Create() already releases its old handle. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::ReInitADIndicators(CIndicators *indicators)
{
bool result = true;
if(m_useADCumulativeDelta)
result = InitADCumulativeDelta(indicators, false) && result;
if(m_useADShorteningOfThrust)
result = InitADShorteningOfThrust(indicators, false) && result;
if(m_useADWyckoffEventStream)
result = InitADWyckoffEventStream(indicators, false) && result;
if(m_useADWyckoffFailedStructure)
result = InitADWyckoffFailedStructure(indicators, false) && result;
if(m_useADWyckoffSignificantBarInversion)
result = InitADWyckoffSignificantBarInversion(indicators, false) && result;
if(m_useMA)
result = InitMA(indicators, false) && result;
if(m_useRSI)
result = InitRSI(indicators, false) && result;
if(m_useMACD)
result = InitMACDFeature(indicators, false) && result;
if(m_useIchimoku)
result = InitIchimoku(indicators, false) && result;
//--- Indicator params just changed, so every cached feature row is now stale (the feature values
//--- depend on these indicators; the LABELS do not - they come from ADZigZag - so the label cache is
//--- deliberately left intact and reused). Without this, a tuner candidate would silently train and be
//--- scored on the PREVIOUS candidate's features. Cheap: just flags rows for lazy recompute on next read.
ArrayInitialize(m_featureCacheHasValue, false);
return result;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::ResizeBuffers(int barIndex)
{
// The Ichimoku feature's Chikou term reads m_Close at idx + ichiKijun (see its block in
// BufferTempDataCompute() for why that direction, and only that direction, is lookahead-free), which
// is further back than any other consumer of the close series reaches. Grow the close buffer to match
// when that feature is on, so the oldest requested bars resolve from real data instead of tripping
// that block's EMPTY_VALUE guard and being rejected as unusable examples.
int closeBars = m_useIchimoku ? barIndex + m_indicatorTuner.ichiKijun : barIndex;
if(!m_Open.BufferResize(barIndex) || !m_Close.BufferResize(closeBars) || !m_High.BufferResize(barIndex) || !m_Low.BufferResize(barIndex))
return false;
if(m_useVolumes)
{
if(!m_Volumes.BufferResize(barIndex))
return false;
}
// Unconditional - see InitTime()'s call site in InitIndicators() for why m_Time must always be live.
if(!m_Time.BufferResize(barIndex))
return false;
if(m_useMA)
{
if(!m_MA.BufferResize(barIndex))
return false;
}
if(m_useRSI)
{
if(!m_RSI.BufferResize(barIndex))
return false;
}
if(m_useMACD)
{
if(!m_MACDFeature.BufferResize(barIndex))
return false;
}
if(m_useIchimoku)
{
// + m_indicatorTuner.ichiKijun: the cloud reads reach that many bars FURTHER back than every other
// indicator here does (see the m_useIchimoku feature block for why the offset exists), so sizing
// this buffer to barIndex alone would leave the oldest requested bars' cloud values unavailable.
if(!m_Ichimoku.BufferResize(barIndex + m_indicatorTuner.ichiKijun))
return false;
}
// Unconditional (not gated by m_useATR): the ATR-normalization in BufferTempData() reads
// m_ATR.Main() regardless of whether ATR is enabled as an explicit extra input feature -
// m_useATR only controls that feature-count opt-in (see InitIndicators()'s "already init in the
// base class" comment), not whether ATR data itself needs to be kept live.
if(!m_ATR.BufferResize(barIndex))
return false;
// Unconditional, same reasoning as m_ATR above - m_ADZigZag is the training-label source (see its
// declaration comment), not an opt-in feature, so it's never gated by an m_use* flag.
if(!m_ADZigZag.BufferResize(barIndex))
return false;
if(m_useADCumulativeDelta)
{
if(!m_ADCumulativeDelta.BufferResize(barIndex))
return false;
}
if(m_useADShorteningOfThrust)
{
if(!m_ADShorteningOfThrust.BufferResize(barIndex))
return false;
}
if(m_useADWyckoffEventStream)
{
if(!m_ADWyckoffEventStream.BufferResize(barIndex))
return false;
}
if(m_useADWyckoffFailedStructure)
{
if(!m_ADWyckoffFailedStructure.BufferResize(barIndex))
return false;
}
if(m_useADWyckoffSignificantBarInversion)
{
if(!m_ADWyckoffSignificantBarInversion.BufferResize(barIndex))
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::RefreshData()
{
// CSeries/CIndicator::Refresh() is void - there is no per-call success/failure signal to
// propagate here. The real data-validity check happens downstream, per value, in
// BufferTempDataCompute() (EMPTY_VALUE / atr<=0 guards) - this function's job is only to ask
// every buffer to refresh, unconditionally, before that per-value check runs.
m_Open.Refresh(OBJ_ALL_PERIODS);
m_Close.Refresh(OBJ_ALL_PERIODS);
m_High.Refresh(OBJ_ALL_PERIODS);
m_Low.Refresh(OBJ_ALL_PERIODS);
if(m_useVolumes)
{
m_Volumes.Refresh(OBJ_ALL_PERIODS);
}
// Unconditional - see InitTime()'s call site in InitIndicators() for why m_Time must always be live.
m_Time.Refresh(OBJ_ALL_PERIODS);
if(m_useMA)
{
m_MA.Refresh(OBJ_ALL_PERIODS);
}
if(m_useRSI)
{
m_RSI.Refresh(OBJ_ALL_PERIODS);
}
if(m_useMACD)
{
m_MACDFeature.Refresh(OBJ_ALL_PERIODS);
}
if(m_useIchimoku)
{
m_Ichimoku.Refresh(OBJ_ALL_PERIODS);
}
// Unconditional - see the matching BufferResize() comment above.
m_ATR.Refresh(OBJ_ALL_PERIODS);
m_ADZigZag.Refresh(OBJ_ALL_PERIODS);
if(m_useADCumulativeDelta)
{
m_ADCumulativeDelta.Refresh(OBJ_ALL_PERIODS);
}
if(m_useADShorteningOfThrust)
{
m_ADShorteningOfThrust.Refresh(OBJ_ALL_PERIODS);
}
if(m_useADWyckoffEventStream)
{
m_ADWyckoffEventStream.Refresh(OBJ_ALL_PERIODS);
}
if(m_useADWyckoffFailedStructure)
{
m_ADWyckoffFailedStructure.Refresh(OBJ_ALL_PERIODS);
}
if(m_useADWyckoffSignificantBarInversion)
{
m_ADWyckoffSignificantBarInversion.Refresh(OBJ_ALL_PERIODS);
}
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Cache-or-compute wrapper around BufferTempDataCompute(): a given |
//| now-relative bar index's feature vector is invariant until the |
//| next candle close (see m_featureCache's declaration comment), so |
//| a cache hit just replays the m_neuronsCount values already |
//| computed for this idx straight into TempData instead of re- |
//| deriving them from price/ATR/AD-indicator buffers again. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::BufferTempData(int idx)
{
int width = m_neuronsCount;
bool cacheable = (idx >= 0 && idx < ArraySize(m_featureCacheHasValue) && width > 0);
if(cacheable && m_featureCacheHasValue[idx])
{
if(!m_featureCacheValid[idx])
return false;
int base = idx * width;
for(int f = 0; f < width; f++)
if(!TempData.Add(m_featureCache[base + f]))
return false;
return true;
}
int startTotal = TempData.Total();
bool ok = BufferTempDataCompute(idx);
if(cacheable)
{
m_featureCacheHasValue[idx] = true;
m_featureCacheValid[idx] = ok;
if(ok)
{
int base = idx * width;
int count = TempData.Total() - startTotal;
for(int f = 0; f < count && f < width; f++)
m_featureCache[base + f] = TempData.At(startTotal + f);
}
}
return ok;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::BufferTempDataCompute(int idx)
{
double open = m_Open.GetData(idx);
double close = m_Close.GetData(idx);
double high = m_High.GetData(idx);
double low = m_Low.GetData(idx);
MqlDateTime sTime;
TimeToStruct(m_Time.GetData(idx), sTime);
if(open == EMPTY_VALUE)
return false;
// ATR-normalize every raw-price-unit feature below instead of feeding e.g. 0.0005 on EURUSD vs.
// 50.0 on a JPY pair or an index straight into the network - with Adam and hardcoded, scale-
// sensitive activations (TANH saturates, PRELU's 0.01 leak only means anything relative to the
// input's own scale), an unnormalized feature either vanishes into rounding noise or dominates
// the weighted sum depending on which symbol/timeframe happens to be loaded. Dividing by the
// bar's own ATR expresses every price-based feature as "fraction of typical volatility", which
// is comparable across symbols/timeframes and centered near zero. No ATR reading yet (e.g. the
// first few bars of history) means every price feature this bar would be meaningless - reject
// the bar via the same "return false" convention as the EMPTY_VALUE check above.
double atr = m_ATR.Main(idx);
if(atr <= 0.0 || atr == EMPTY_VALUE)
return false;
if(!TempData.Add((close - open) / atr) ||
!TempData.Add((high - open) / atr) ||
!TempData.Add((low - open) / atr) ||
// Explicit bullish/bearish flag - (close-open)/atr already encodes direction *and* magnitude
// together, which asks the network to disentangle "which way" from "how much" out of a single
// continuous value. Giving direction its own clean +1/-1/0 signal removes that ambiguity.
!TempData.Add(close > open ? 1.0 : (close < open ? -1.0 : 0.0)))
{
return false;
}
if(m_useSwingContext)
{
// Most recent CONFIRMED swing pivot as of bar idx - "confirmed" meaning at least
// m_swingConfirmationBars MORE bars have closed after it, the exact same repainting embargo
// AdvanceZigZagLabelState() applies on the label side (see m_swingConfirmationBars' and
// m_useSwingContext's declaration comments). Skipping this embargo here - e.g. reading
// m_ADZigZag's raw current buffer value instead - would leak information a live bar at idx
// could never actually have had yet, since ZigZag's most recent 1-3 legs are still provisional
// and can be revised as new bars arrive.
int pivotIdx = -1;
double pivotPrice = 0.0;
bool pivotIsLow = false;
if(!FindConfirmedZigZagPivot(idx + MathMax(m_swingConfirmationBars, 1), pivotIdx, pivotPrice, pivotIsLow))
{
// No confirmed pivot within the scan cap (e.g. right at the start of available history) -
// this is legitimately "no swing context yet", not bad/missing data, so a neutral 0-fill
// keeps the bar usable rather than rejecting it outright like the ATR/EMPTY_VALUE guards do.
if(!TempData.Add(0.0) || !TempData.Add(0.0) || !TempData.Add(0.0) || !TempData.Add(0.0) || !TempData.Add(0.0))
return false;
}
else
{
// Direction of the CURRENT leg: the last confirmed pivot being a bottom means price has been
// rising away from it (an up-leg) ever since, and vice versa - same +1/-1 convention as the
// bullish/bearish flag above, just at swing scale instead of single-bar scale.
double direction = pivotIsLow ? 1.0 : -1.0;
// How far price has travelled since that pivot, ATR-normalized and signed (+ve above the
// pivot price, -ve below) - clamped generously since an extended trending leg has no natural
// ceiling the way a single bar's range does.
double distSincePivot = MathMax(-10.0, MathMin(10.0, (close - pivotPrice) / atr));
// Magnitude of the PRIOR completed leg (the pivot immediately before pivotIdx) - a scale
// reference for "is the current move big or small relative to the last full swing". No
// additional embargo needed here (see FindConfirmedZigZagPivot()'s declaration comment) -
// anything at or before an already-confirmed pivot is necessarily even older.
int priorPivotIdx = -1;
double priorPivotPrice = 0.0;
bool priorPivotIsLow = false;
bool havePrior = FindConfirmedZigZagPivot(pivotIdx + 1, priorPivotIdx, priorPivotPrice, priorPivotIsLow);
double priorLegMagnitude = havePrior ? MathMax(0.0, MathMin(10.0, MathAbs(pivotPrice - priorPivotPrice) / atr)) : 0.0;
// Retracement/extension ratio (current distance relative to the prior leg's own size) -
// Fibonacci-style relative position, often more informative than either raw magnitude alone
// since it's comparable across both quiet and volatile regimes. 0 when there's no prior leg
// to compare against yet.
double retracementRatio = (havePrior && priorLegMagnitude > 0.0001) ?
MathMax(-5.0, MathMin(5.0, distSincePivot / priorLegMagnitude)) : 0.0;
// Swing age (bars since the pivot) - a maturity/exhaustion proxy, same +/- style clamp
// convention as the volume-ratio feature below.
double barsSincePivot = MathMax(0.0, MathMin(5.0, (double)(pivotIdx - idx) / 100.0));
if(!TempData.Add(direction) ||
!TempData.Add(distSincePivot) ||
!TempData.Add(priorLegMagnitude) ||
!TempData.Add(retracementRatio) ||
!TempData.Add(barsSincePivot))
return false;
}
// Recent price-action context (4 features), computed from CLOSED bars at idx or older only -
// no ZigZag confirmation, so no repainting and NO embargo, and never stale, unlike the five
// pivot-anchored features above whose confirmed anchor is always >= m_swingConfirmationBars
// (~100) bars old. Those describe the OLD structure well but say nothing about the recent leg
// the bar actually sits in - which is exactly what's needed to tell a genuine reversal at a
// range extreme from a mid-trend bar that merely looks like a bottom/top (the "clustered
// counter-trend signals" failure mode). These locate the bar within its recent range and
// trend so the network can learn that a directional call belongs at an extreme of an extended
// move, not anywhere the local candle shape resembles a pivot. All windows walk toward OLDER
// bars (increasing index), so nothing here can see the future.
double hi20 = high, lo20 = low, hi50 = high, lo50 = low;
double sum20 = close, oldestClose20 = close;
int cnt20 = 1;
for(int w = 1; w < 50; w++)
{
int j = idx + w;
double jc = m_Close.GetData(j);
double jh = m_High.GetData(j);
double jl = m_Low.GetData(j);
// ran off the oldest edge of loaded history (out-of-range reads back as 0/EMPTY_VALUE) -
// use whatever window we gathered so far rather than rejecting the bar; a shorter early-
// history window is degraded-but-usable, same spirit as the pivot 0-fill above.
if(jh == EMPTY_VALUE || jh <= 0.0 || jl <= 0.0)
break;
if(jh > hi50)
hi50 = jh;
if(jl < lo50)
lo50 = jl;
if(w < 20)
{
if(jh > hi20)
hi20 = jh;
if(jl < lo20)
lo20 = jl;
sum20 += jc;
oldestClose20 = jc;
cnt20++;
}
}
// Donchian position: where close sits inside the recent high/low range, rescaled to [-1,+1]
// (-1 = at the range low / bottom candidate, +1 = at the range high / top candidate, 0 = mid-
// range / mid-trend). Two scales - a short 20-bar and a medium 50-bar view - so the network
// sees both local and swing-scale extremity. 0 (mid) when the range is degenerate.
double range20 = hi20 - lo20;
double range50 = hi50 - lo50;
double donchPos20 = (range20 > 0.0) ? ((close - lo20) / range20 - 0.5) * 2.0 : 0.0;
double donchPos50 = (range50 > 0.0) ? ((close - lo50) / range50 - 0.5) * 2.0 : 0.0;
// Net directional displacement over the recent window, ATR-normalized and signed - the
// prevailing-trend strength/direction the counter-trend clusters were ignoring.
double recentReturn = MathMax(-10.0, MathMin(10.0, (close - oldestClose20) / atr));
// Distance from the recent mean (SMA), ATR-normalized - a stretch/exhaustion proxy distinct
// from the net return (a move can be far from its mean with little net displacement, or vice
// versa); genuine reversals tend to be over-extended from equilibrium.
double smaExtension = MathMax(-10.0, MathMin(10.0, (close - sum20 / cnt20) / atr));
if(!TempData.Add(donchPos20) ||
!TempData.Add(donchPos50) ||
!TempData.Add(recentReturn) ||
!TempData.Add(smaExtension))
return false;
}
if(m_useVolumes)
{
// Relative change, not raw tick/volume delta - trading activity magnitude also varies wildly
// across symbols/timeframes, so the previous bar's own volume is used as the scale reference,
// same logic as ATR-normalizing price above. Guard against a zero previous-bar volume (e.g. a
// holiday-thin session) instead of dividing by it. Clamped to +/-5 - unlike the ATR-normalized
// price features, this ratio has no natural ceiling (a thin previous bar, e.g. 1 tick, followed
// by a normal one produces a huge outlier), and every other feature fed into the network is
// already bounded to roughly a single-digit range.
double prevVolume = m_Volumes.Main(idx + 1);
double volumeDelta = m_Volumes.Main(idx) - prevVolume;
double volumeChangeRatio = prevVolume > 0.0 ? volumeDelta / prevVolume : 0.0;
if(!TempData.Add(MathMax(-5.0, MathMin(5.0, volumeChangeRatio))))
return false;
}
if(m_useTime)
{
// Normalize time (cyclical encoding)
if(!TempData.Add(sin(2 * M_PI * sTime.hour / 24.0)))
return false;
if(!TempData.Add(cos(2 * M_PI * sTime.hour / 24.0)))
return false;
if(!TempData.Add(sin(2 * M_PI * sTime.day_of_week / 7.0)))
return false;
if(!TempData.Add(cos(2 * M_PI * sTime.day_of_week / 7.0)))
return false;
if(!TempData.Add(sin(2 * M_PI * sTime.mon / 12.0)))
return false;
if(!TempData.Add(cos(2 * M_PI * sTime.mon / 12.0)))
return false;
}
if(m_useATR)
{
// ATR/close (volatility as a fraction of price), not raw ATR - the raw absolute value is
// itself unnormalized (e.g. ~0.0012 on EURUSD vs. ~1.5 on gold, and drifts over time even on
// one symbol as its price level changes), which is exactly the kind of scale-dependent
// feature this whole normalization pass is fixing everywhere else.
if(!TempData.Add(close != 0.0 ? atr / close : 0.0))
return false;
}
if(m_useMA)
{
// Same ATR-normalized distance-from-level convention as the base OHLC-from-open features above,
// just measured against the MA instead of the bar's own open - lets the network read where
// price sits relative to the same MA Signals\SignalMA.mqh votes on. Plus the MA's own
// bar-over-bar change (also ATR-normalized, since the MA lives in price units and ATR is
// already this codebase's scale reference for that - see m_useMA's declaration comment for why
// this isn't volume's previous-bar-ratio scheme instead).
double maNow = m_MA.GetData(0, idx);
double maPrev = m_MA.GetData(0, idx + 1);
if(maNow == EMPTY_VALUE || maPrev == EMPTY_VALUE)
return false;
if(!TempData.Add((open - maNow) / atr) ||
!TempData.Add((high - maNow) / atr) ||
!TempData.Add((low - maNow) / atr) ||
!TempData.Add((close - maNow) / atr) ||
!TempData.Add((maNow - maPrev) / atr))
return false;
}
if(m_useRSI)
{
// Already a 0-100 oscillator - /100 is the only transform needed to match the rest of the
// feature vector's scale (see m_useRSI's declaration comment).
double rsiNow = m_RSI.Main(idx);
if(rsiNow == EMPTY_VALUE)
return false;
if(!TempData.Add(rsiNow / 100.0))
return false;
}
if(m_useMACD)
{
// Main and signal lines are price-domain differences of two EMAs, so the same ATR normalization
// every other price-unit feature here uses applies unchanged. The third value is the histogram
// (main - signal): algebraically derivable from the first two, but handed over explicitly for the
// same reason the bullish/bearish flag is handed to the network alongside (close-open)/atr - a
// value the network would otherwise have to learn to subtract is better given directly, and the
// histogram (momentum ACCELERATION) is the one term nothing else in this vector carries.
double macdMain = m_MACDFeature.Main(idx);
double macdSignal = m_MACDFeature.Signal(idx);
if(macdMain == EMPTY_VALUE || macdSignal == EMPTY_VALUE)
return false;
if(!TempData.Add(macdMain / atr) ||
!TempData.Add(macdSignal / atr) ||
!TempData.Add((macdMain - macdSignal) / atr))
return false;
}
if(m_useIchimoku)
{
// LOOKAHEAD, the one thing that matters in this block. MT5's iIchimoku does NOT pre-shift its
// buffers - it stores raw per-bar values and shifts only the DRAWING (Ichimoku.mq5 sets
// PLOT_SHIFT=+Kijun on the Senkou A/B cloud plot and -Kijun on the Chikou plot). In series
// indexing that means:
// - SenkouSpan*(i) is computed FROM bar i and drawn Kijun bars into the FUTURE, so the cloud
// actually sitting under bar idx is SenkouSpan*(idx + kijun) - built from bar idx+kijun and
// older, hence strictly past data. Reading SenkouSpan*(idx) as "the cloud here" is the classic
// Ichimoku backtest bug and would leak Kijun bars of future information into every example.
// - SenkouSpan*(idx) with NO offset IS legitimate as the PROJECTED cloud - the part of the chart
// already drawn ahead of the current bar. A live bar at idx genuinely knows it (it is computed
// from bar idx), which is why it appears below as its own feature rather than being avoided.
// - ChinkouSpan(i) is just Close(i) drawn at i+Kijun, so the Chikou plotted AT bar idx would be
// Close(idx - kijun) - a FUTURE bar. It is never read. The lookahead-free statement of the same
// reading is "how far is this close from the close Kijun bars ago", the last feature below.
// Signals\SignalIchimoku.mqh's class comment documents the identical convention for the vote side.
int kijunShift = m_indicatorTuner.ichiKijun;
double tenkan = m_Ichimoku.TenkanSen(idx);
double kijun = m_Ichimoku.KijunSen(idx);
double spanA = m_Ichimoku.SenkouSpanA(idx + kijunShift); // cloud AS PLOTTED AT bar idx
double spanB = m_Ichimoku.SenkouSpanB(idx + kijunShift);
double futureSpanA = m_Ichimoku.SenkouSpanA(idx); // cloud projected AHEAD of bar idx
double futureSpanB = m_Ichimoku.SenkouSpanB(idx);
double closeLagRef = m_Close.GetData(idx + kijunShift); // Chikou reference, never idx - kijunShift
if(tenkan == EMPTY_VALUE || kijun == EMPTY_VALUE ||
spanA == EMPTY_VALUE || spanB == EMPTY_VALUE ||
futureSpanA == EMPTY_VALUE || futureSpanB == EMPTY_VALUE ||
closeLagRef == EMPTY_VALUE || closeLagRef <= 0.0)
return false;
if(!TempData.Add((close - tenkan) / atr) || // distance to the fast line
!TempData.Add((close - kijun) / atr) || // distance to the equilibrium line
!TempData.Add((tenkan - kijun) / atr) || // TK spread: sign = cross state, size = conviction
!TempData.Add((close - spanA) / atr) || // distance to each cloud edge, so the network can
!TempData.Add((close - spanB) / atr) || // place price above / inside / below the cloud
!TempData.Add((spanA - spanB) / atr) || // signed cloud thickness here: sign = regime, size = strength
!TempData.Add((futureSpanA - futureSpanB) / atr) || // same for the projected cloud - the "twist" ahead
!TempData.Add((close - closeLagRef) / atr)) // Chikou displacement, in its lookahead-free form
return false;
}
if(m_useNews)
{
// Event proximity + impact only - see this member's declaration comment and
// System\NewsRelevance.mqh's ImpactWeightedProximity() for why the forward-looking half
// (searchForward=true) isn't lookahead bias despite being computed for a historical bar.
datetime barTime = m_Time.GetData(idx);
double newsRecency = ImpactWeightedProximity(m_symbol.Name(), barTime, m_newsFeatureWindowMinutes, false);
double newsProximity = ImpactWeightedProximity(m_symbol.Name(), barTime, m_newsFeatureWindowMinutes, true);
if(!TempData.Add(newsRecency) || !TempData.Add(newsProximity))
return false;
}
if(m_useADCumulativeDelta)
{
// buffers: 0=Pressure, 1=CumulativeDelta, 2=BullishPressure, 3=BearishPressure, 4=Absorption, 5=Initiative.
// CumulativeDelta (buffer 1) is now cumulativeDelta/sumVolume clamped +/-2 (same scale as every
// other buffer here, see ADCumulativeDelta.mq5) - a pure order-flow-imbalance ratio, distinct from
// Pressure (buffer 0), which is this same term further adjusted by Initiative/Absorption.
if(!TempData.Add(m_ADCumulativeDelta.GetData(0, idx)) || // Pressure
!TempData.Add(m_ADCumulativeDelta.GetData(1, idx)) || // CumulativeDelta
!TempData.Add(m_ADCumulativeDelta.GetData(2, idx)) || // BullishPressure
!TempData.Add(m_ADCumulativeDelta.GetData(3, idx)) || // BearishPressure
!TempData.Add(m_ADCumulativeDelta.GetData(4, idx)) || // Absorption
!TempData.Add(m_ADCumulativeDelta.GetData(5, idx))) // Initiative
return false;
}
if(m_useADShorteningOfThrust)
{
// buffers: 0=SOT, 1=SOTEffortRegime, 2=SOTConfirmation, 3=SOTPushRegime
if(!TempData.Add(m_ADShorteningOfThrust.GetData(0, idx)) || // SOT
!TempData.Add(m_ADShorteningOfThrust.GetData(1, idx)) || // SOTEffortRegime
!TempData.Add(m_ADShorteningOfThrust.GetData(2, idx)) || // SOTConfirmation
!TempData.Add(m_ADShorteningOfThrust.GetData(3, idx))) // SOTPushRegime
return false;
}
if(m_useADWyckoffEventStream)
{
// buffers: 0=EventCode, 1=EventPhase, 2=ZoneTop, 3=ZoneBottom, 4=EventPrice, 5=StructuralPhase,
// 6=CHoCHTrendToRange, 7=CHoCHRangeToTrend, 8=SlopeAccumulationBullish, 9=SlopeAccumulationBearish,
// 10=SlopeDistributionBullish, 11=SlopeDistributionBearish, 12=Reaccumulation, 13=Redistribution.
// Buffer 4 (EventPrice) is deliberately skipped below - per the indicator's own source
// (ADWyckoffEventStream.mq5: "BufColor[wi]=(ev!=0)?C[i]:0;"), it's just this bar's close price
// echoed back when an event fires (0 otherwise), kept only so a charting/backtesting tool like
// StrategyQuant can anchor an arrow to a price. It carries no information the network doesn't
// already have (EventCode already flags whether an event fired; the close is already in the
// base OHLC features), and normalizing it as a "distance from close" like ZoneTop/ZoneBottom
// below would be actively wrong: it's close-close=0 on event bars but 0-close=-close (a raw,
// ATR-blown-up price) on every other bar - a huge, meaningless outlier feature.
// Buffer 1 (EventPhase) is also deliberately skipped - per the indicator's own source
// (ADWyckoffEventStream.mq5: "BufPhase[wi]=(double)ev;", the exact same `ev` variable written to
// BufEvent one line above), it's a byte-for-byte duplicate of EventCode, not a distinct phase
// reading - the real phase signal is StructuralPhase (buffer 5), already included below.
if(!TempData.Add(m_ADWyckoffEventStream.GetData(0, idx)) || // EventCode
!TempData.Add((m_ADWyckoffEventStream.GetData(2, idx) - close) / atr) || // ZoneTop
!TempData.Add((m_ADWyckoffEventStream.GetData(3, idx) - close) / atr) || // ZoneBottom
!TempData.Add(m_ADWyckoffEventStream.GetData(5, idx)) || // StructuralPhase
!TempData.Add(m_ADWyckoffEventStream.GetData(6, idx)) || // CHoCHTrendToRange
!TempData.Add(m_ADWyckoffEventStream.GetData(7, idx)) || // CHoCHRangeToTrend
!TempData.Add(m_ADWyckoffEventStream.GetData(8, idx)) || // SlopeAccumulationBullish
!TempData.Add(m_ADWyckoffEventStream.GetData(9, idx)) || // SlopeAccumulationBearish
!TempData.Add(m_ADWyckoffEventStream.GetData(10, idx)) || // SlopeDistributionBullish
!TempData.Add(m_ADWyckoffEventStream.GetData(11, idx)) || // SlopeDistributionBearish
!TempData.Add(m_ADWyckoffEventStream.GetData(12, idx)) || // Reaccumulation
!TempData.Add(m_ADWyckoffEventStream.GetData(13, idx))) // Redistribution
return false;
}
if(m_useADWyckoffFailedStructure)
{
// buffers: 0=Value, 1=BullishStructuralFailure, 2=BearishStructuralFailure, 3=FailedAccumulation, 4=FailedDistribution
if(!TempData.Add(m_ADWyckoffFailedStructure.GetData(0, idx)) || // Value
!TempData.Add(m_ADWyckoffFailedStructure.GetData(1, idx)) || // BullishStructuralFailure
!TempData.Add(m_ADWyckoffFailedStructure.GetData(2, idx)) || // BearishStructuralFailure
!TempData.Add(m_ADWyckoffFailedStructure.GetData(3, idx)) || // FailedAccumulation
!TempData.Add(m_ADWyckoffFailedStructure.GetData(4, idx))) // FailedDistribution
return false;
}
if(m_useADWyckoffSignificantBarInversion)
{
// buffers: 0=SignificantBarQuality, 1=BullishSignificantBar, 2=BearishSignificantBar, 3=BullishControlFlip, 4=BearishControlFlip
if(!TempData.Add(m_ADWyckoffSignificantBarInversion.GetData(0, idx)) || // SignificantBarQuality
!TempData.Add(m_ADWyckoffSignificantBarInversion.GetData(1, idx)) || // BullishSignificantBar
!TempData.Add(m_ADWyckoffSignificantBarInversion.GetData(2, idx)) || // BearishSignificantBar
!TempData.Add(m_ADWyckoffSignificantBarInversion.GetData(3, idx)) || // BullishControlFlip
!TempData.Add(m_ADWyckoffSignificantBarInversion.GetData(4, idx))) // BearishControlFlip
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Initialize Open indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitOpen(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_Open)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_Open.Create(m_symbol.Name(), m_period))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize Close indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitClose(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_Close)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_Close.Create(m_symbol.Name(), m_period))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize High indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitHigh(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_High)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_High.Create(m_symbol.Name(), m_period))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize Low indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitLow(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_Low)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_Low.Create(m_symbol.Name(), m_period))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize Time indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitTime(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_Time)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_Time.Create(m_symbol.Name(), m_period))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize Volumes indicators. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitVolumes(CIndicators * indicators)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(!indicators.Add(GetPointer(m_Volumes)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object
if(!m_Volumes.Create(m_symbol.Name(), m_period, VolumeData))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize MA indicator (feature use - see m_useMA). Period comes |
//| from m_indicatorTuner.maPeriod, not the raw PeriodMA input - it |
//| starts equal to it (see CADIndicatorTuner's constructor) but may |
//| diverge once AutoTuneIndicators actually searches a trial. The |
//| Classic Signals MA vote is unaffected - see m_useMA's declaration |
//| comment. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitMA(CIndicators * indicators, bool addToCollection)
{
if(indicators == NULL)
return (false);
if(addToCollection && !indicators.Add(GetPointer(m_MA)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- unified MA custom indicator (CustomIndicators\ADMovingAverage.mq5); type AND period are both tuner-
//--- driven (m_indicatorTuner.maType/maPeriod). params[1..] mirror the indicator's own input order.
MqlParam params[9];
params[0].type = TYPE_STRING; params[0].string_value = WARRIOR_CI("ADMovingAverage");
params[1].type = TYPE_INT; params[1].integer_value = m_indicatorTuner.maType; // InpType
params[2].type = TYPE_INT; params[2].integer_value = m_indicatorTuner.maPeriod; // InpPeriod
params[3].type = TYPE_INT; params[3].integer_value = PRICE_CLOSE; // InpAppliedPrice
params[4].type = TYPE_DOUBLE; params[4].double_value = 0.85; // InpOffset (ALMA)
params[5].type = TYPE_DOUBLE; params[5].double_value = 6.0; // InpSigma (ALMA)
params[6].type = TYPE_DOUBLE; params[6].double_value = 0.7; // InpVolumeFactor (T3)
params[7].type = TYPE_DOUBLE; params[7].double_value = 0.001; // InpProcessNoise (Kalman)
params[8].type = TYPE_DOUBLE; params[8].double_value = 0.1; // InpMeasurementNoise (Kalman)
if(!m_MA.Create(m_symbol.Name(), m_period, IND_CUSTOM, 9, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_MA.NumBuffers(1);
return (true);
}
//+------------------------------------------------------------------+
//| Initialize RSI indicator (feature use - see m_useRSI). Period |
//| comes from m_indicatorTuner.rsiPeriod - see InitMA()'s comment. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitRSI(CIndicators * indicators, bool addToCollection)
{
if(indicators == NULL)
return (false);
if(addToCollection && !indicators.Add(GetPointer(m_RSI)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
if(!m_RSI.Create(m_symbol.Name(), m_period, m_indicatorTuner.rsiPeriod, PRICE_CLOSE))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
return (true);
}
//+------------------------------------------------------------------+
//| Initialize MACD indicator (feature use - see m_useMACD). Periods |
//| come from m_indicatorTuner.macdFast/macdSlow/macdSignal - see |
//| InitMA()'s comment for the "starts at the input, may diverge once |
//| the tuner searches" split, and note the Classic Signals MACD vote |
//| (Signals\SignalMACD.mqh) keeps its own separate instance. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitMACDFeature(CIndicators * indicators, bool addToCollection)
{
if(indicators == NULL)
return (false);
if(addToCollection && !indicators.Add(GetPointer(m_MACDFeature)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
if(!m_MACDFeature.Create(m_symbol.Name(), m_period, m_indicatorTuner.macdFast, m_indicatorTuner.macdSlow,
m_indicatorTuner.macdSignal, PRICE_CLOSE))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
return (true);
}
//+------------------------------------------------------------------+
//| Initialize Ichimoku indicator (feature use - see m_useIchimoku). |
//| Periods come from m_indicatorTuner.ichiTenkan/ichiKijun/ |
//| ichiSenkou - see InitMA()'s comment. The Classic Signals Ichimoku |
//| vote (Signals\SignalIchimoku.mqh) keeps its own instance. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitIchimoku(CIndicators * indicators, bool addToCollection)
{
if(indicators == NULL)
return (false);
if(addToCollection && !indicators.Add(GetPointer(m_Ichimoku)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
if(!m_Ichimoku.Create(m_symbol.Name(), m_period, m_indicatorTuner.ichiTenkan, m_indicatorTuner.ichiKijun,
m_indicatorTuner.ichiSenkou))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD Cumulative Delta (CustomIndicators\ADCumulativeDelta.mq5) |
//| Loaded via iCustom/CiCustom, not a built-in Ci* class - the compiled |
//| indicator must be present under MQL5\Indicators\ (see |
//| ExtractCustomIndicators() in Warrior_EA.mq5). Uses the indicator's |
//| own input defaults; 6 output buffers, one TempData feature each. |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADCumulativeDelta(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADCumulativeDelta)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADCumulativeDelta.mq5's own input order exactly
MqlParam params[11];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADCumulativeDelta");
params[1].type = TYPE_INT;
params[1].integer_value = m_indicatorTuner.adCumDelta.lookback; // InpLookbackPeriod
params[2].type = TYPE_DOUBLE;
params[2].double_value = m_indicatorTuner.adCumDelta.volClimax; // InpVolumeClimaxMultiplier
params[3].type = TYPE_DOUBLE;
params[3].double_value = m_indicatorTuner.adCumDelta.volHigh; // InpVolumeHighMultiplier
params[4].type = TYPE_DOUBLE;
params[4].double_value = m_indicatorTuner.adCumDelta.rangeClimax; // InpRangeClimaxMultiplier
params[5].type = TYPE_DOUBLE;
params[5].double_value = m_indicatorTuner.adCumDelta.rangeSignificant; // InpRangeSignificantMult
params[6].type = TYPE_DOUBLE;
params[6].double_value = m_indicatorTuner.adCumDelta.stVolRatio; // InpSTVolumeRatio
params[7].type = TYPE_DOUBLE;
params[7].double_value = m_indicatorTuner.adCumDelta.atrMult; // InpATRMultiplier
params[8].type = TYPE_INT;
params[8].integer_value = 0; // InpContextMode - DO NOT tune
params[9].type = TYPE_INT;
params[9].integer_value = 5; // InpSessionType - DO NOT tune
params[10].type = TYPE_INT;
params[10].integer_value = 1; // InpSessionCount - DO NOT tune
if(!m_ADCumulativeDelta.Create(m_symbol.Name(), m_period, IND_CUSTOM, 11, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_ADCumulativeDelta.NumBuffers(6);
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD Shortening of Thrust (CustomIndicators\ADShorteningOfThrust.mq5) |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADShorteningOfThrust(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADShorteningOfThrust)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADShorteningOfThrust.mq5's own input order exactly
MqlParam params[7];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADShorteningOfThrust");
params[1].type = TYPE_INT;
params[1].integer_value = m_indicatorTuner.adSOT.thrustLookback; // InpThrustLookback
params[2].type = TYPE_INT;
params[2].integer_value = m_indicatorTuner.adSOT.minImpulses; // InpMinImpulses
params[3].type = TYPE_DOUBLE;
params[3].double_value = m_indicatorTuner.adSOT.sotThreshold; // InpSOTThreshold
params[4].type = TYPE_INT;
params[4].integer_value = 0; // InpContextMode - DO NOT tune
params[5].type = TYPE_INT;
params[5].integer_value = 5; // InpSessionType - DO NOT tune
params[6].type = TYPE_INT;
params[6].integer_value = 1; // InpSessionCount - DO NOT tune
if(!m_ADShorteningOfThrust.Create(m_symbol.Name(), m_period, IND_CUSTOM, 7, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_ADShorteningOfThrust.NumBuffers(4);
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD Wyckoff Event Stream (CustomIndicators\ADWyckoffEventStream.mq5) |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADWyckoffEventStream(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADWyckoffEventStream)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADWyckoffEventStream.mq5's own input order exactly
MqlParam params[12];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADWyckoffEventStream");
params[1].type = TYPE_INT;
params[1].integer_value = m_indicatorTuner.adWES.lookback; // InpLookback
params[2].type = TYPE_INT;
params[2].integer_value = m_indicatorTuner.adWES.zigzag; // InpZigZag
params[3].type = TYPE_DOUBLE;
params[3].double_value = m_indicatorTuner.adWES.volClimax; // InpVolClimax
params[4].type = TYPE_DOUBLE;
params[4].double_value = m_indicatorTuner.adWES.volHigh; // InpVolHigh
params[5].type = TYPE_DOUBLE;
params[5].double_value = m_indicatorTuner.adWES.rangeClimax; // InpRangeClimax
params[6].type = TYPE_DOUBLE;
params[6].double_value = m_indicatorTuner.adWES.rangeSignificant; // InpRangeSignificant
params[7].type = TYPE_DOUBLE;
params[7].double_value = m_indicatorTuner.adWES.stVolRatio; // InpSTVolRatio
params[8].type = TYPE_DOUBLE;
params[8].double_value = m_indicatorTuner.adWES.atr; // InpATR
params[9].type = TYPE_INT;
params[9].integer_value = 0; // InpContextMode - DO NOT tune
params[10].type = TYPE_INT;
params[10].integer_value = 5; // InpSessionType - DO NOT tune
params[11].type = TYPE_INT;
params[11].integer_value = 1; // InpSessionCount - DO NOT tune
if(!m_ADWyckoffEventStream.Create(m_symbol.Name(), m_period, IND_CUSTOM, 12, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_ADWyckoffEventStream.NumBuffers(14);
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD Wyckoff Failed Structure (CustomIndicators\ADWyckoffFailedStructure.mq5) |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADWyckoffFailedStructure(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADWyckoffFailedStructure)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADWyckoffFailedStructure.mq5's own input order exactly
MqlParam params[12];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADWyckoffFailedStructure");
params[1].type = TYPE_INT;
params[1].integer_value = m_indicatorTuner.adWFS.lookback; // InpLookbackPeriod
params[2].type = TYPE_INT;
params[2].integer_value = m_indicatorTuner.adWFS.zigzagStrength; // InpZigZagStrength
params[3].type = TYPE_DOUBLE;
params[3].double_value = m_indicatorTuner.adWFS.volClimax; // InpVolumeClimaxMultiplier
params[4].type = TYPE_DOUBLE;
params[4].double_value = m_indicatorTuner.adWFS.volHigh; // InpVolumeHighMultiplier
params[5].type = TYPE_DOUBLE;
params[5].double_value = m_indicatorTuner.adWFS.rangeClimax; // InpRangeClimaxMultiplier
params[6].type = TYPE_DOUBLE;
params[6].double_value = m_indicatorTuner.adWFS.rangeSignificant; // InpRangeSignificantMult
params[7].type = TYPE_DOUBLE;
params[7].double_value = m_indicatorTuner.adWFS.stVolRatio; // InpSTVolumeRatio
params[8].type = TYPE_DOUBLE;
params[8].double_value = m_indicatorTuner.adWFS.atrMult; // InpATRMultiplier
params[9].type = TYPE_INT;
params[9].integer_value = 0; // InpContextMode - DO NOT tune
params[10].type = TYPE_INT;
params[10].integer_value = 5; // InpSessionType - DO NOT tune
params[11].type = TYPE_INT;
params[11].integer_value = 1; // InpSessionCount - DO NOT tune
if(!m_ADWyckoffFailedStructure.Create(m_symbol.Name(), m_period, IND_CUSTOM, 12, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_ADWyckoffFailedStructure.NumBuffers(5);
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD Wyckoff Significant Bar Inversion (CustomIndicators\ADWyckoffSignificantBarInversion.mq5) |
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADWyckoffSignificantBarInversion(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADWyckoffSignificantBarInversion)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADWyckoffSignificantBarInversion.mq5's own input order exactly
MqlParam params[8];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADWyckoffSignificantBarInversion");
params[1].type = TYPE_INT;
params[1].integer_value = m_indicatorTuner.adWSBI.lookback; // InpLookback
params[2].type = TYPE_DOUBLE;
params[2].double_value = m_indicatorTuner.adWSBI.rangeSignificant; // InpRangeSignificant
params[3].type = TYPE_DOUBLE;
params[3].double_value = m_indicatorTuner.adWSBI.volumeHigh; // InpVolumeHigh
params[4].type = TYPE_DOUBLE;
params[4].double_value = m_indicatorTuner.adWSBI.atr; // InpATR
params[5].type = TYPE_INT;
params[5].integer_value = 0; // InpContextMode - DO NOT tune
params[6].type = TYPE_INT;
params[6].integer_value = 5; // InpSessionType - DO NOT tune
params[7].type = TYPE_INT;
params[7].integer_value = 1; // InpSessionCount - DO NOT tune
if(!m_ADWyckoffSignificantBarInversion.Create(m_symbol.Name(), m_period, IND_CUSTOM, 8, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
m_ADWyckoffSignificantBarInversion.NumBuffers(5);
//--- ok
return (true);
}
//+------------------------------------------------------------------+
//| Initialize AD ZigZag (CustomIndicators\ADZigZag.mq5) - the |
//| training-label source (see m_ADZigZag's declaration comment). |
//| Always run at its own stock defaults (Depth=12, Deviation=5, |
//| Backstep=3) - unlike the AD* feature indicators above, this has |
//| no tunable-param struct and is never touched by AutoTuneIndicators.|
//+------------------------------------------------------------------+
bool CExpertSignalAIBase::InitADZigZag(CIndicators * indicators, bool addToCollection)
{
//--- check pointer
if(indicators == NULL)
return (false);
//--- add object to collection
if(addToCollection && !indicators.Add(GetPointer(m_ADZigZag)))
{
printf(__FUNCTION__ + ": error adding object");
return (false);
}
//--- initialize object; params[1..] mirror ADZigZag.mq5's own input order exactly - stock defaults,
//--- intentionally not sourced from a tunable params struct (see this function's declaration comment)
MqlParam params[4];
params[0].type = TYPE_STRING;
params[0].string_value = WARRIOR_CI("ADZigZag");
params[1].type = TYPE_INT;
params[1].integer_value = 12; // InpDepth
params[2].type = TYPE_INT;
params[2].integer_value = 5; // InpDeviation
params[3].type = TYPE_INT;
params[3].integer_value = 3; // InpBackstep
if(!m_ADZigZag.Create(m_symbol.Name(), m_period, IND_CUSTOM, 4, params))
{
printf(__FUNCTION__ + ": error initializing object");
return (false);
}
// Must match ADZigZag.mq5's #property indicator_buffers exactly (3: main ZigZag buffer + 2
// internal INDICATOR_CALCULATIONS buffers), even though only buffer 0 is ever read via
// GetData() - see the working AD Wyckoff indicators' InitAD*() for the same pattern.
m_ADZigZag.NumBuffers(3);
//--- ok
return (true);
}
#endif // WARRIOR_AIBASE_FEATURES_MQH