diff --git a/Include/MyIncludes/RSIH_Calculator.mqh b/Include/MyIncludes/RSIH_Calculator.mqh new file mode 100644 index 0000000..388e324 --- /dev/null +++ b/Include/MyIncludes/RSIH_Calculator.mqh @@ -0,0 +1,166 @@ +//+------------------------------------------------------------------+ +//| RSIH_Calculator.mqh | +//| Calculation engine for Ehlers' RSI with Hann Windowing. | +//| Copyright 2025, xxxxxxxx | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" + +#include + +//+==================================================================+ +//| | +//| CLASS 1: CRSIHCalculator (Base Class) | +//| | +//+==================================================================+ +class CRSIHCalculator + { +protected: + int m_period; + double m_price[]; + + virtual bool PreparePriceSeries(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]); + +public: + CRSIHCalculator(void) {}; + virtual ~CRSIHCalculator(void) {}; + + bool Init(int period); + void Calculate(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], double &rsih_buffer[]); + }; + +//+------------------------------------------------------------------+ +bool CRSIHCalculator::Init(int period) + { + m_period = (period < 2) ? 2 : period; + return true; + } + +//+------------------------------------------------------------------+ +void CRSIHCalculator::Calculate(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], double &rsih_buffer[]) + { + if(rates_total < m_period + 1) + return; + if(!PreparePriceSeries(rates_total, price_type, open, high, low, close)) + return; + +// Full recalculation for stability + for(int i = m_period; i < rates_total; i++) + { + double cu = 0.0; + double cd = 0.0; + + // Inner loop to calculate Hann-windowed CU and CD + for(int j = 1; j <= m_period; j++) + { + // Ehlers' formula uses count from 1 to Period. In MQL5 array terms, this is price[i-j+1] vs price[i-j] + // But his EasyLanguage code seems to use Close[count-1] - Close[count], which is a bit ambiguous. + // We will follow the more standard momentum calculation: price[current] - price[previous] + // Let's use a consistent diff calculation: price[i-j] vs price[i-j-1] + double diff = m_price[i - j + 1] - m_price[i - j]; + + // Hann Windowing Weight + // Ehlers' formula: (1 - Cosine(360*count / (RSILength + 1))) + // In radians: 1.0 - cos(2 * M_PI * j / (m_period + 1.0)) + double weight = 1.0 - cos(2 * M_PI * j / (m_period + 1.0)); + + if(diff > 0) + cu += diff * weight; + else + cd += -diff * weight; + } + + if(cu + cd > 0) + rsih_buffer[i] = (cu - cd) / (cu + cd); + else + rsih_buffer[i] = 0; + } + } + +//+------------------------------------------------------------------+ +bool CRSIHCalculator::PreparePriceSeries(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) + { + ArrayResize(m_price, rates_total); + switch(price_type) + { + case PRICE_CLOSE: + ArrayCopy(m_price, close, 0, 0, rates_total); + break; + case PRICE_OPEN: + ArrayCopy(m_price, open, 0, 0, rates_total); + break; + case PRICE_HIGH: + ArrayCopy(m_price, high, 0, 0, rates_total); + break; + case PRICE_LOW: + ArrayCopy(m_price, low, 0, 0, rates_total); + break; + case PRICE_MEDIAN: + for(int i=0; i