Warrior_EA/Signals/SignalMA.mqh

346 lines
16 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| SignalMA.mqh |
//| Copyright 2000-2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Expert\ExpertSignalCustom.mqh"
#include "..\Variables\IndicatorResources.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:
CiCustom m_ma; // unified MA indicator (CustomIndicators\ADMovingAverage.mq5)
//--- adjusted parameters
int m_ma_period; // the "period of averaging" parameter of the indicator
int m_ma_type; // unified MA type (MA_TYPE_PRESETS); values 0..3 == ENUM_MA_METHOD
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; }
//--- NOTE: no Shift() here. The unified ADMovingAverage indicator has no shift parameter, so the
//--- wizard-API setter this class used to carry only ever wrote a member nothing read - a silent
//--- no-op for any caller that set it. Removed rather than left as a lie about what is configurable.
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);
feat(meta): self-contained corpus - the META chart sweeps the real classic ladders over its own history The user should not need a tester corpus run per symbol. Every pattern condition in Signals\Signal{MA,RSI,MACD,Ichimoku}.mqh anchors its reads on `int idx = StartIndex()` with zero hardcoded indices (verified), so a name-hiding StartIndex override + EvalShift(i) on CExpertSignalCustom makes the EXACT live ladder code answer "what would you have fired at bar i" - the silent-divergence trap that justified the DB corpus does not exist on this path, and neither do the GMT-offset ambiguity, the DB row caps, or the wipe procedure. - CExpertSignalCustom: m_evalShift + StartIndex()/EvalShift() + SweepPrepare(bars) (deep-resizes the shared price series); the four classic signal classes override SweepPrepare to deep-resize their own indicator buffers. - CSignalMETA::BuildCorpusBySweep: per bar x per source filter, run Direction() shifted, harvest the per-side pattern slots + netVote into the same corpus arrays the DB loader fills; entry=bar open so MetaPrepareEra's resolution matches at offset +0 with zero price error. DB corpus remains the fallback when classic filters are disabled. - Warrior_EA.mq5: META gets the enabled classic filters as candidate sources (family ids match the descriptor one-hot). - UseDatabaseRanking default false -> true (user request): a META chart journals + ranks out of the box. Workflow per symbol is now: attach ONE chart with AIType=META (optionally Meta_ExportDataset=true for the offline pool) - candidates, labels, training and export all happen in place, ~10 seconds of sweep instead of a tester run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:19:43 -04:00
//--- 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)); }
fix(signals): revive a dead MA model, and demote Sanyaku from state to event Two defects surfaced by research/test_classic.py, both verified fixed by re-running the transcription against 178k bars of EURUSD H1. CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so DiffMA(i) = a * (Close(i) - MA(i+1)) DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1)) are positive multiples of one quantity and always share a sign. Model 1 asks for a close BELOW a RISING average, which is precisely the combination that identity forbids: 0.000% of bars, either direction, any symbol. The MQL5 standard library this was ported from defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars. CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing STATES conjoined with no transition term, so it held across long stretches - and being last in the if-chain at the top weight, the module's highest-conviction reading was also its most common one, overwriting all eight event models below it on a quarter of all bars. The old comment rejected an event form because "demanding all three flip on the same bar would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1) fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback. Neither pattern showed edge before or after; this is about the models meaning what they say and the vote not being dominated by a constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
//--- 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 with the shipped MA_TYPE_EMA default the model could never fire on any bar of
//--- any symbol. (The MQL5 standard library this was ported from defaults to MODE_SMA, where the
//--- two quantities are merely correlated, so the bug arrived with the EMA default rather than
//--- with the port.) 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),
m_ma_type(MA_TYPE_EMA),
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);
}
//--- 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);
}
//--- initialize object: the unified MA custom indicator (CustomIndicators\ADMovingAverage.mq5). params[1..]
//--- mirror that indicator's own input order exactly (Type,Period,AppliedPrice,Offset,Sigma,VolumeFactor,
//--- ProcessNoise,MeasurementNoise). The advanced-type params use the indicator defaults for the classic
//--- vote; only Type/Period/Applied are driven from this signal's settings.
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_ma_type; // InpType
params[2].type = TYPE_INT; params[2].integer_value = m_ma_period; // InpPeriod
params[3].type = TYPE_INT; params[3].integer_value = m_ma_applied; // 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);
//--- 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
fix(signals): revive a dead MA model, and demote Sanyaku from state to event Two defects surfaced by research/test_classic.py, both verified fixed by re-running the transcription against 178k bars of EURUSD H1. CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so DiffMA(i) = a * (Close(i) - MA(i+1)) DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1)) are positive multiples of one quantity and always share a sign. Model 1 asks for a close BELOW a RISING average, which is precisely the combination that identity forbids: 0.000% of bars, either direction, any symbol. The MQL5 standard library this was ported from defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars. CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing STATES conjoined with no transition term, so it held across long stretches - and being last in the if-chain at the top weight, the module's highest-conviction reading was also its most common one, overwriting all eight event models below it on a quarter of all bars. The old comment rejected an event form because "demanding all three flip on the same bar would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1) fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback. Neither pattern showed edge before or after; this is about the models meaning what they say and the vote not being dominated by a constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
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
fix(signals): revive a dead MA model, and demote Sanyaku from state to event Two defects surfaced by research/test_classic.py, both verified fixed by re-running the transcription against 178k bars of EURUSD H1. CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so DiffMA(i) = a * (Close(i) - MA(i+1)) DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1)) are positive multiples of one quantity and always share a sign. Model 1 asks for a close BELOW a RISING average, which is precisely the combination that identity forbids: 0.000% of bars, either direction, any symbol. The MQL5 standard library this was ported from defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars. CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing STATES conjoined with no transition term, so it held across long stretches - and being last in the if-chain at the top weight, the module's highest-conviction reading was also its most common one, overwriting all eight event models below it on a quarter of all bars. The old comment rejected an event form because "demanding all three flip on the same bar would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1) fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback. Neither pattern showed edge before or after; this is about the models meaning what they say and the vote not being dominated by a constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
//--- (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
fix(signals): revive a dead MA model, and demote Sanyaku from state to event Two defects surfaced by research/test_classic.py, both verified fixed by re-running the transcription against 178k bars of EURUSD H1. CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so DiffMA(i) = a * (Close(i) - MA(i+1)) DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1)) are positive multiples of one quantity and always share a sign. Model 1 asks for a close BELOW a RISING average, which is precisely the combination that identity forbids: 0.000% of bars, either direction, any symbol. The MQL5 standard library this was ported from defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars. CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing STATES conjoined with no transition term, so it held across long stretches - and being last in the if-chain at the top weight, the module's highest-conviction reading was also its most common one, overwriting all eight event models below it on a quarter of all bars. The old comment rejected an event form because "demanding all three flip on the same bar would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1) fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback. Neither pattern showed edge before or after; this is about the models meaning what they say and the vote not being dominated by a constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
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
fix(signals): revive a dead MA model, and demote Sanyaku from state to event Two defects surfaced by research/test_classic.py, both verified fixed by re-running the transcription against 178k bars of EURUSD H1. CSignalMA model 1 could never fire. For any recursive average - and MA_TYPE_EMA is the shipped default - MA(i) = a*Close(i) + (1-a)*MA(i+1), so DiffMA(i) = a * (Close(i) - MA(i+1)) DiffCloseMA(i) = (1-a) * (Close(i) - MA(i+1)) are positive multiples of one quantity and always share a sign. Model 1 asks for a close BELOW a RISING average, which is precisely the combination that identity forbids: 0.000% of bars, either direction, any symbol. The MQL5 standard library this was ported from defaults to MODE_SMA, where the two are merely correlated - the bug arrived with the EMA default, not with the port. Reading the slope one bar back (DiffMAPrev) breaks the tie for every MA type while keeping the model's stated meaning. Now fires on 7.92% of bars. CSignalIchimoku model 11 fired on 27% of bars at weight 100. Sanyaku is three standing STATES conjoined with no transition term, so it held across long stretches - and being last in the if-chain at the top weight, the module's highest-conviction reading was also its most common one, overwriting all eight event models below it on a quarter of all bars. The old comment rejected an event form because "demanding all three flip on the same bar would fire almost never" - true, but that is not the alternative. Kouten is the TURN: the ALIGNMENT transitions, and only one role need change for it to. Testing !Sanyaku(idx+1) fires once per aligned stretch. Now 2.17%, in line with Kumo breakout (2.4%) and the strong TK cross (1.1%). DataReady() extended one bar deeper to cover the lookback. Neither pattern showed edge before or after; this is about the models meaning what they say and the vote not being dominated by a constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:34 -04:00
//--- (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;
}
}
//+------------------------------------------------------------------+