forked from animatedread/Warrior_EA
MA: CustomIndicators\ADMovingAverage is replaced by the built-in iMA (CiMA) on both consumers - the classic vote and the NN MA input feature. This drops the five advanced types ALMA/DEMA/ZLEMA/T3/Kalman, which have no iMA equivalent; MA_TYPE_PRESETS is now ENUM_MA_METHOD's own codes and the tuner searches all four. It also removes a documented failure mode: a custom indicator's depth is bounded by TERMINAL_MAXBARS, and m_MA was the one whose feature block REJECTS the bar on a short read - the "feature 25 fails on every bar" incident of 2026-08-17. A built-in is served at any depth. MIGRATION. SMA moves from code 5 to 0, so persisted type codes change meaning. SanitizeMaType() is the single validity rule; TunedPeriods records now carry a version field and a v1 record remaps 5..8 -> 0..3, falling back to SMA for a stored advanced type (unrecoverable - old 0..4 are indistinguishable from valid new codes). Existing .nnw files re-key on their own, because MA_Type is hashed into the topology fingerprint, so models retrain rather than silently running on different MA values. EXPECT A FULL RETRAIN. ZigZag: ADZigZag was a byte-identical rename of MetaQuotes' Examples\ZigZag - verified by normalising identifiers and stripping comments, 233 significant lines each with only renamed symbols differing. It now loads the stock one, so nothing is bundled and MetaQuotes' fixes arrive without a rebuild here. Both #resource entries are gone. Classic_Shift: a new input, the BAR the four classic votes evaluate on (0 = forming, 1 = last closed, default 1). One implementation on CExpertSignalCustom, inherited by all four rather than repeated per module. Defaults to a sentinel meaning "unset", so the AI signals and the aggregate keep the stock every_tick rule and their feature/label alignment is untouched. The META corpus sweep still takes precedence. CExpertBase::StartIndex turns out to be virtual, so this is a real override, not the name-hiding the old comment claimed. Not compiled - MetaEditor compile pending. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
340 lines
15 KiB
MQL5
340 lines
15 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| SignalMA.mqh |
|
|
//| Copyright 2000-2023, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#include "..\Expert\ExpertSignalCustom.mqh"
|
|
// wizard description start
|
|
//+------------------------------------------------------------------+
|
|
//| Description of the class |
|
|
//| Title=Signals of indicator 'Moving Average' |
|
|
//| Type=SignalAdvanced |
|
|
//| Name=Moving Average |
|
|
//| ShortName=MA |
|
|
//| Class=CSignalMA |
|
|
//| Page=signal_ma |
|
|
//| Parameter=PeriodMA,int,10,Period of averaging |
|
|
//| Parameter=Shift,int,0,Time shift |
|
|
//| Parameter=Method,ENUM_MA_METHOD,MODE_SMA,Method of averaging |
|
|
//| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series |
|
|
//+------------------------------------------------------------------+
|
|
// wizard description end
|
|
//+------------------------------------------------------------------+
|
|
//| Class CSignalMA. |
|
|
//| Purpose: Class of generator of trade signals based on |
|
|
//| the 'Moving Average' indicator. |
|
|
//| Is derived from the CExpertSignalCustom class. |
|
|
//+------------------------------------------------------------------+
|
|
class CSignalMA : public CExpertSignalCustom
|
|
{
|
|
protected:
|
|
CiMA m_ma; // built-in moving average (iMA)
|
|
//--- adjusted parameters
|
|
int m_ma_period; // the "period of averaging" parameter of the indicator
|
|
int m_ma_type; // MA_TYPE_PRESETS == ENUM_MA_METHOD codes, passed to iMA as-is
|
|
ENUM_APPLIED_PRICE m_ma_applied; // the "object of averaging" parameter of the indicator
|
|
//--- "weights" of market models (0-100)
|
|
int m_pattern_0; // model 0 "price is on the necessary side from the indicator"
|
|
int m_pattern_1; // model 1 "price crossed the indicator with opposite direction"
|
|
int m_pattern_2; // model 2 "price crossed the indicator with the same direction"
|
|
int m_pattern_3; // model 3 "piercing"
|
|
|
|
public:
|
|
CSignalMA(void);
|
|
~CSignalMA(void);
|
|
//--- methods of setting adjustable parameters
|
|
void PeriodMA(int value) { m_ma_period = value; }
|
|
//--- Shift() is the base class's (CExpertSignalCustom): the BAR this vote is evaluated on, not
|
|
//--- iMA's ma_shift, which this class pins to 0. See InitMA().
|
|
void Method(int value) { m_ma_type = value; }
|
|
void Applied(ENUM_APPLIED_PRICE value) { m_ma_applied = value; }
|
|
//--- methods of adjusting "weights" of market models
|
|
void Pattern_0(int value) { m_pattern_0 = value; }
|
|
void Pattern_1(int value) { m_pattern_1 = value; }
|
|
void Pattern_2(int value) { m_pattern_2 = value; }
|
|
void Pattern_3(int value) { m_pattern_3 = value; }
|
|
virtual void ApplyPatternWeight(int patternNumber, int weight);
|
|
//--- method of verification of settings
|
|
virtual bool ValidationSettings(void);
|
|
//--- method of creating the indicator and timeseries
|
|
virtual bool InitIndicators(CIndicators *indicators);
|
|
//--- methods of checking if the market models are formed
|
|
virtual int LongCondition(void);
|
|
virtual int ShortCondition(void);
|
|
//--- deep-history readiness for the meta candidate sweep - see CExpertSignalCustom::SweepPrepare
|
|
virtual bool SweepPrepare(const int bars) override
|
|
{
|
|
if(!CExpertSignalCustom::SweepPrepare(bars))
|
|
return false;
|
|
bool ok = m_ma.BufferResize(bars);
|
|
m_ma.Refresh(-1);
|
|
return ok;
|
|
}
|
|
|
|
protected:
|
|
//--- method of initialization of the indicator
|
|
bool InitMA(CIndicators *indicators);
|
|
//--- methods of getting data
|
|
double MA(int ind) { return(m_ma.GetData(0, ind)); }
|
|
double DiffMA(int ind) { return(MA(ind) - MA(ind + 1)); }
|
|
//--- Slope as it stood at the PREVIOUS bar, i.e. not yet influenced by bar `ind`'s own close.
|
|
//--- Model 1 needs this and DiffMA() cannot serve: for any RECURSIVE average (EMA/SMMA, and the
|
|
//--- exponential members of MA_TYPE_PRESETS) MA(ind) = a*Close(ind) + (1-a)*MA(ind+1), so
|
|
//--- DiffMA(ind) = a * (Close(ind) - MA(ind + 1))
|
|
//--- DiffCloseMA(ind) = (1-a) * (Close(ind) - MA(ind + 1))
|
|
//--- are positive multiples of the same quantity and therefore ALWAYS share a sign. Model 1 asks
|
|
//--- for "close below a RISING average", which is exactly the sign combination that identity
|
|
//--- forbids - so under EMA or SMMA the model could never fire on any bar of any symbol. Both are
|
|
//--- still selectable and tuner-reachable, so this is live, not historical. Reading the slope one
|
|
//--- bar back breaks the algebraic tie for every MA type while keeping the model's stated meaning:
|
|
//--- a pull-back closing against an established trend.
|
|
double DiffMAPrev(int ind) { return(MA(ind + 1) - MA(ind + 2)); }
|
|
double DiffOpenMA(int ind) { return(Open(ind) - MA(ind)); }
|
|
double DiffHighMA(int ind) { return(High(ind) - MA(ind)); }
|
|
double DiffLowMA(int ind) { return(Low(ind) - MA(ind)); }
|
|
double DiffCloseMA(int ind) { return(Close(ind) - MA(ind)); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CSignalMA::CSignalMA(void) : m_ma_period(12),
|
|
//--- SMA to match the MA_Type input seed. Warrior_EA.mq5 always pushes g_TunedMaType over this,
|
|
//--- so the default only shows if the signal is constructed outside that path - it should still
|
|
//--- agree with the documented default rather than quietly disagreeing.
|
|
m_ma_type(MA_TYPE_SMA),
|
|
m_ma_applied(PRICE_CLOSE),
|
|
m_pattern_0(10),
|
|
m_pattern_1(10),
|
|
m_pattern_2(60),
|
|
m_pattern_3(60)
|
|
{
|
|
m_id = "MA";
|
|
m_pattern_count = 4;
|
|
//--- initialization of protected data
|
|
m_used_series = USE_SERIES_OPEN + USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_CLOSE;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSignalMA::~CSignalMA(void)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Validation settings protected data. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalMA::ValidationSettings(void)
|
|
{
|
|
//--- validation settings of additional filters
|
|
if(!CExpertSignalCustom::ValidationSettings())
|
|
return(false);
|
|
//--- initial data checks
|
|
if(m_ma_period <= 0)
|
|
{
|
|
printf(__FUNCTION__ + ": period MA must be greater than 0");
|
|
return(false);
|
|
}
|
|
//--- iMA silently returns an invalid handle for a method outside ENUM_MA_METHOD, which shows up much
|
|
//--- later as a vote that never fires. Fail here instead, where the cause is still visible.
|
|
if(m_ma_type < MODE_SMA || m_ma_type > MODE_LWMA)
|
|
{
|
|
printf(__FUNCTION__ + ": MA type %d is not an ENUM_MA_METHOD value", m_ma_type);
|
|
return(false);
|
|
}
|
|
//--- ok
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Create indicators. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalMA::InitIndicators(CIndicators *indicators)
|
|
{
|
|
//--- check pointer
|
|
if(indicators == NULL)
|
|
return(false);
|
|
//--- initialization of indicators and timeseries of additional filters
|
|
if(!CExpertSignalCustom::InitIndicators(indicators))
|
|
return(false);
|
|
//--- create and initialize MA indicator
|
|
if(!InitMA(indicators))
|
|
return(false);
|
|
//--- ok
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize MA indicators. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalMA::InitMA(CIndicators *indicators)
|
|
{
|
|
//--- check pointer
|
|
if(indicators == NULL)
|
|
return(false);
|
|
//--- add object to collection
|
|
if(!indicators.Add(GetPointer(m_ma)))
|
|
{
|
|
printf(__FUNCTION__ + ": error adding object");
|
|
return(false);
|
|
}
|
|
//--- built-in iMA. ma_shift is fixed at 0: it displaces the AVERAGE along the time axis, which is not
|
|
//--- what this EA means by shift - the bar a condition is evaluated on is CExpertSignalCustom::Shift().
|
|
if(!m_ma.Create(m_symbol.Name(), m_period, m_ma_period, 0, (ENUM_MA_METHOD)m_ma_type, m_ma_applied))
|
|
{
|
|
printf(__FUNCTION__ + ": error initializing object");
|
|
return(false);
|
|
}
|
|
//--- ok
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| "Voting" that price will grow. |
|
|
//+------------------------------------------------------------------+
|
|
int CSignalMA::LongCondition(void)
|
|
{
|
|
int result = 0;
|
|
int idx = StartIndex();
|
|
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
|
|
if(DiffCloseMA(idx) < 0.0)
|
|
{
|
|
//--- the close price is below the indicator
|
|
if(IS_PATTERN_USAGE(1) && DiffOpenMA(idx) > 0.0 && DiffMAPrev(idx) > 0.0)
|
|
{
|
|
//--- the open price is above the indicator (i.e. there was an intersection), but the indicator is directed upwards
|
|
//--- (measured at the previous bar - see DiffMAPrev() for why DiffMA() makes this unsatisfiable)
|
|
result = m_pattern_1;
|
|
m_active_pattern = "Pattern_1";
|
|
//--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
|
|
m_base_price = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- the close price is above the indicator (the indicator has no objections to buying)
|
|
if(IS_PATTERN_USAGE(0))
|
|
{
|
|
result = m_pattern_0;
|
|
m_active_pattern = "Pattern_0";
|
|
}
|
|
//--- if the indicator is directed upwards
|
|
if(DiffMA(idx) > 0.0)
|
|
{
|
|
if(DiffOpenMA(idx) < 0.0)
|
|
{
|
|
//--- if the model 2 is used
|
|
if(IS_PATTERN_USAGE(2))
|
|
{
|
|
//--- the open price is below the indicator (i.e. there was an intersection)
|
|
result = m_pattern_2;
|
|
m_active_pattern = "Pattern_2";
|
|
//--- suggest to enter the market at the "roll back"
|
|
m_base_price = m_symbol.NormalizePrice(MA(idx));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- if the model 3 is used and the open price is above the indicator
|
|
if(IS_PATTERN_USAGE(3) && DiffLowMA(idx) < 0.0)
|
|
{
|
|
//--- the low price is below the indicator
|
|
result = m_pattern_3;
|
|
m_active_pattern = "Pattern_3";
|
|
//--- consider that this is a formed "piercing" and suggest to enter the market at the current price
|
|
m_base_price = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(result != 0)
|
|
{
|
|
m_active_direction = "Buy";
|
|
}
|
|
//--- return the result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| "Voting" that price will fall. |
|
|
//+------------------------------------------------------------------+
|
|
int CSignalMA::ShortCondition(void)
|
|
{
|
|
int result = 0;
|
|
int idx = StartIndex();
|
|
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
|
|
if(DiffCloseMA(idx) > 0.0)
|
|
{
|
|
//--- the close price is above the indicator
|
|
if(IS_PATTERN_USAGE(1) && DiffOpenMA(idx) < 0.0 && DiffMAPrev(idx) < 0.0)
|
|
{
|
|
//--- the open price is below the indicator (i.e. there was an intersection), but the indicator is directed downwards
|
|
//--- (measured at the previous bar - see DiffMAPrev() for why DiffMA() makes this unsatisfiable)
|
|
result = m_pattern_1;
|
|
m_active_pattern = "Pattern_1";
|
|
//--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
|
|
m_base_price = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- the close price is below the indicator (the indicator has no objections to buying)
|
|
if(IS_PATTERN_USAGE(0))
|
|
{
|
|
result = m_pattern_0;
|
|
m_active_pattern = "Pattern_0";
|
|
}
|
|
//--- the indicator is directed downwards
|
|
if(DiffMA(idx) < 0.0)
|
|
{
|
|
if(DiffOpenMA(idx) > 0.0)
|
|
{
|
|
//--- if the model 2 is used
|
|
if(IS_PATTERN_USAGE(2))
|
|
{
|
|
//--- the open price is above the indicator (i.e. there was an intersection)
|
|
result = m_pattern_2;
|
|
m_active_pattern = "Pattern_2";
|
|
//--- suggest to enter the market at the "roll back"
|
|
m_base_price = m_symbol.NormalizePrice(MA(idx));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- if the model 3 is used and the open price is below the indicator
|
|
if(IS_PATTERN_USAGE(3) && DiffHighMA(idx) > 0.0)
|
|
{
|
|
//--- the high price is above the indicator
|
|
result = m_pattern_3;
|
|
m_active_pattern = "Pattern_3";
|
|
//--- consider that this is a formed "piercing" and suggest to enter the market at the current price
|
|
m_base_price = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(result != 0)
|
|
{
|
|
m_active_direction = "Sell";
|
|
}
|
|
//--- return the result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Set the specified pattern's weight to the specified value |
|
|
//+------------------------------------------------------------------+
|
|
void CSignalMA::ApplyPatternWeight(int patternNumber, int weight)
|
|
{
|
|
switch(patternNumber)
|
|
{
|
|
default:
|
|
break;
|
|
case 0:
|
|
Pattern_0(weight);
|
|
break;
|
|
case 1:
|
|
Pattern_1(weight);
|
|
break;
|
|
case 2:
|
|
Pattern_2(weight);
|
|
break;
|
|
case 3:
|
|
Pattern_3(weight);
|
|
break;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|