//+------------------------------------------------------------------+ //| SignalRSI.mqh | //| Copyright 2000-2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Expert\WarriorSignal.mqh" #include "OscillatorDivergence.mqh" // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Signals of oscillator 'Relative Strength Index' | //| Type=SignalAdvanced | //| Name=Relative Strength Index | //| ShortName=RSI | //| Class=CSignalRSI | //| Page=signal_rsi | //| Parameter=PeriodRSI,int,14,Period of calculation | //| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| CSignalRSI's side of the IOscillatorDivergenceSource view - | //| MQL5 has no multiple inheritance, so the divergence detector is | //| reached through this thin adapter instead of a common base. | //+------------------------------------------------------------------+ class CSignalRSI; class CSignalRSIDivergenceSource : public IOscillatorDivergenceSource { private: CSignalRSI *m_owner; public: void Bind(CSignalRSI *owner) { m_owner = owner; } virtual double DivergenceOscillatorValue(int ind) override; virtual double DivergencePriceLow(int start, int count, int &index) override; virtual double DivergencePriceHigh(int start, int count, int &index) override; }; //+------------------------------------------------------------------+ //| Class CSignalRSI. | //| Purpose: Class of generator of trade signals based on | //| the 'Relative Strength Index' oscillator. | //| Is derived from the CWarriorSignal class. | //+------------------------------------------------------------------+ class CSignalRSI : public CWarriorSignal { protected: CiRSI m_rsi; // object-oscillator //--- adjusted parameters int m_periodRSI; // the "period of calculation" parameter of the oscillator ENUM_APPLIED_PRICE m_applied; // the "prices series" parameter of the oscillator //--- "weights" of market models (0-100) int m_pattern_0; // model 0 "the oscillator has required direction" int m_pattern_1; // model 1 "reverse behind the level of overbuying/overselling" int m_pattern_2; // model 2 "divergence of the oscillator and price" int m_pattern_3; // model 3 "double divergence of the oscillator and price" //--- shared extremum/divergence bit-map detector (see Signals/OscillatorDivergence.mqh) CSignalRSIDivergenceSource m_divergenceSource; CDivergenceDetector m_divergence; public: CSignalRSI(void); ~CSignalRSI(void); //--- methods of setting adjustable parameters void PeriodRSI(int value) { m_periodRSI = value; } void Applied(ENUM_APPLIED_PRICE value) { m_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 CWarriorSignal::SweepPrepare //--- IOscillatorDivergenceSource surface for m_divergenceSource (see OscillatorDivergence.mqh); //--- public because the adapter is a separate object, not a subclass double DivergenceOscillatorValue(int ind) { return(RSI(ind)); } double DivergencePriceLow(int start, int count, int &index) { return(m_low.MinValue(start, count, index)); } double DivergencePriceHigh(int start, int count, int &index) { return(m_high.MaxValue(start, count, index)); } protected: //--- method of initialization of the oscillator bool InitRSI(CIndicators *indicators); //--- methods of getting data double RSI(int ind) { return(m_rsi.Main(ind)); } double DiffRSI(int ind) { return(RSI(ind) - RSI(ind + 1)); } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSignalRSI::CSignalRSI(void) : m_periodRSI(14), m_applied(PRICE_CLOSE), m_pattern_0(10), m_pattern_1(60), m_pattern_2(80), m_pattern_3(100) { m_divergenceSource.Bind(GetPointer(this)); m_divergence.Bind(GetPointer(m_divergenceSource)); m_id = "RSI"; //--- Confirmation pattern(s): weight 10, never traded alone, never ranked. m_confirm_mask = 1<<0; m_pattern_count = 4; //--- initialization of protected data m_used_series = USE_SERIES_HIGH + USE_SERIES_LOW; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSignalRSI::~CSignalRSI(void) { } //+------------------------------------------------------------------+ //| Validation settings protected data. | //+------------------------------------------------------------------+ bool CSignalRSI::ValidationSettings(void) { //--- validation settings of additional filters if(!CWarriorSignal::ValidationSettings()) return(false); //--- initial data checks if(m_periodRSI <= 0) { printf(__FUNCTION__ + ": period of the RSI oscillator must be greater than 0"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Create indicators. | //+------------------------------------------------------------------+ bool CSignalRSI::InitIndicators(CIndicators *indicators) { //--- check pointer if(indicators == NULL) return(false); //--- initialization of indicators and timeseries of additional filters if(!CWarriorSignal::InitIndicators(indicators)) return(false); //--- create and initialize RSI oscillator if(!InitRSI(indicators)) return(false); //--- ok return(true); } //+------------------------------------------------------------------+ //| Initialize RSI oscillators. | //+------------------------------------------------------------------+ bool CSignalRSI::InitRSI(CIndicators *indicators) { //--- check pointer if(indicators == NULL) return(false); //--- add object to collection if(!indicators.Add(GetPointer(m_rsi))) { printf(__FUNCTION__ + ": error adding object"); return(false); } //--- initialize object if(!m_rsi.Create(m_symbol.Name(), m_period, m_periodRSI, m_applied)) { printf(__FUNCTION__ + ": error initializing object"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| CSignalRSIDivergenceSource - forwards to the owner's public | //| IOscillatorDivergenceSource surface (see the class declaration | //| just above CSignalRSI). | //+------------------------------------------------------------------+ double CSignalRSIDivergenceSource::DivergenceOscillatorValue(int ind) { return(m_owner.DivergenceOscillatorValue(ind)); } double CSignalRSIDivergenceSource::DivergencePriceLow(int start, int count, int &index) { return(m_owner.DivergencePriceLow(start, count, index)); } double CSignalRSIDivergenceSource::DivergencePriceHigh(int start, int count, int &index) { return(m_owner.DivergencePriceHigh(start, count, index)); } //+------------------------------------------------------------------+ //| "Voting" that price will grow. | //+------------------------------------------------------------------+ int CSignalRSI::LongCondition(void) { int result = 0; int idx = StartIndex(); //--- if(DiffRSI(idx) > 0.0) { //--- the oscillator is directed upwards confirming the possibility of price growth if(IS_PATTERN_USAGE(0)) { result = m_pattern_0; m_active_pattern = "Pattern_0"; } // "confirming" signal number 0 //--- if the model 1 is used, search for a reverse of the oscillator upwards behind the level of overselling if(IS_PATTERN_USAGE(1) && DiffRSI(idx + 1) < 0.0 && RSI(idx + 1) < 30.0) { result = m_pattern_1; m_active_pattern = "Pattern_1"; } //--- if model 2 or 3 is used, perform the extended analysis of the oscillator state. //--- NOTE: this used to also test IS_PATTERN_USAGE(4), inherited from the standard library's //--- 5-pattern RSI. This class declares 4 patterns (m_pattern_count = 4, models 0-3), so bit 4 //--- has no weight member and neither branch below can act on it - but m_patterns_usage defaults //--- to all-bits-set, so the term was NOT dead: it let the whole block (and ExtState's scan) //--- run even with models 2 and 3 both switched off, for a result that could never change. if(IS_PATTERN_USAGE(2) || IS_PATTERN_USAGE(3)) { m_divergence.ExtState(idx); //--- search for the "divergence" signal if(IS_PATTERN_USAGE(2) && m_divergence.CompareMaps(1, 1)) // 0000 0001b { result = m_pattern_2; m_active_pattern = "Pattern_2"; } //--- search for the "double divergence" signal if(IS_PATTERN_USAGE(3) && m_divergence.CompareMaps(0x11, 2)) // 0001 0001b { result = m_pattern_3; m_active_pattern = "Pattern_3"; } } } if(result != 0) { m_active_direction = "Buy"; } //--- return the result return(result); } //+------------------------------------------------------------------+ //| "Voting" that price will fall. | //+------------------------------------------------------------------+ int CSignalRSI::ShortCondition(void) { int result = 0; int idx = StartIndex(); //--- if(DiffRSI(idx) < 0.0) { //--- the oscillator is directed downwards confirming the possibility of falling of price if(IS_PATTERN_USAGE(0)) { result = m_pattern_0; m_active_pattern = "Pattern_0"; } //--- if the model 1 is used, search for a reverse of the oscillator downwards behind the level of overbuying if(IS_PATTERN_USAGE(1) && DiffRSI(idx + 1) > 0.0 && RSI(idx + 1) > 70.0) { result = m_pattern_1; m_active_pattern = "Pattern_1"; } // signal number 1 //--- if model 2 or 3 is used, perform the extended analysis of the oscillator state //--- (see LongCondition() for why the old IS_PATTERN_USAGE(4) term was removed) if(IS_PATTERN_USAGE(2) || IS_PATTERN_USAGE(3)) { m_divergence.ExtState(idx); //--- search for the "divergence" signal if(IS_PATTERN_USAGE(2) && m_divergence.CompareMaps(1, 1)) // 0000 0001b { result = m_pattern_2; m_active_pattern = "Pattern_2"; } //--- search for the "double divergence" signal if(IS_PATTERN_USAGE(3) && m_divergence.CompareMaps(0x11, 2)) // 0001 0001b { result = m_pattern_3; m_active_pattern = "Pattern_3"; } } } if(result != 0) { m_active_direction = "Sell"; } //--- return the result return(result); } //+------------------------------------------------------------------+ //| Set the specified pattern's weight to the specified value | //+------------------------------------------------------------------+ void CSignalRSI::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; } } //+------------------------------------------------------------------+