diff --git a/Include/MyIncludes/KeltnerChannel_Calculator.mqh b/Include/MyIncludes/KeltnerChannel_Calculator.mqh new file mode 100644 index 0000000..b89ba7b --- /dev/null +++ b/Include/MyIncludes/KeltnerChannel_Calculator.mqh @@ -0,0 +1,241 @@ +//+------------------------------------------------------------------+ +//| KeltnerChannel_Calculator.mqh| +//| Calculation engine for Standard and Heikin Ashi Keltner Channels.| +//| Copyright 2025, xxxxxxxx | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" + +#include + +//--- Re-use the enum from the main file +enum ENUM_ATR_SOURCE + { + ATR_SOURCE_STANDARD, // Calculate ATR from standard candles + ATR_SOURCE_HEIKIN_ASHI // Calculate ATR from Heikin Ashi candles + }; + +//+==================================================================+ +//| | +//| CLASS 1: CKeltnerChannelCalculator (Base Class) | +//| | +//+==================================================================+ +class CKeltnerChannelCalculator + { +protected: + int m_ma_period, m_atr_period; + ENUM_MA_METHOD m_ma_method; + double m_multiplier; + ENUM_ATR_SOURCE m_atr_source; + + double m_ma_price[]; + + virtual bool PrepareMAPriceSeries(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], ENUM_APPLIED_PRICE price_type); + +public: + CKeltnerChannelCalculator(void) {}; + virtual ~CKeltnerChannelCalculator(void) {}; + + bool Init(int ma_p, ENUM_MA_METHOD ma_m, int atr_p, double mult, ENUM_ATR_SOURCE atr_src); + void Calculate(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], ENUM_APPLIED_PRICE price_type, + double &middle_buffer[], double &upper_buffer[], double &lower_buffer[]); + }; + +//+------------------------------------------------------------------+ +//| CKeltnerChannelCalculator: Initialization | +//+------------------------------------------------------------------+ +bool CKeltnerChannelCalculator::Init(int ma_p, ENUM_MA_METHOD ma_m, int atr_p, double mult, ENUM_ATR_SOURCE atr_src) + { + m_ma_period = (ma_p < 1) ? 1 : ma_p; + m_ma_method = ma_m; + m_atr_period = (atr_p < 1) ? 1 : atr_p; + m_multiplier = (mult <= 0) ? 2.0 : mult; + m_atr_source = atr_src; + return true; + } + +//+------------------------------------------------------------------+ +//| CKeltnerChannelCalculator: Main Calculation Method (Shared Logic)| +//+------------------------------------------------------------------+ +void CKeltnerChannelCalculator::Calculate(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], ENUM_APPLIED_PRICE price_type, + double &middle_buffer[], double &upper_buffer[], double &lower_buffer[]) + { + int start_pos = MathMax(m_ma_period, m_atr_period); + if(rates_total <= start_pos) + return; + + if(!PrepareMAPriceSeries(rates_total, open, high, low, close, price_type)) + return; + + double atr_buffer[], tr[]; + ArrayResize(atr_buffer, rates_total); + ArrayResize(tr, rates_total); + +//--- STEP 1: Calculate True Range based on the selected ATR source + if(m_atr_source == ATR_SOURCE_HEIKIN_ASHI) + { + CHeikinAshi_Calculator ha_calc; + double ha_open[], ha_high[], ha_low[], ha_close[]; + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + ha_calc.Calculate(rates_total, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + for(int i = 1; i < rates_total; i++) + tr[i] = MathMax(ha_high[i], ha_close[i-1]) - MathMin(ha_low[i], ha_close[i-1]); + } + else // ATR_SOURCE_STANDARD + { + for(int i = 1; i < rates_total; i++) + tr[i] = MathMax(high[i], close[i-1]) - MathMin(low[i], close[i-1]); + } + + for(int i = 1; i < rates_total; i++) + { + //--- STEP 2: Calculate ATR (Wilder's smoothing) + if(i == m_atr_period) + { + double sum=0; + for(int j=1; j<=m_atr_period; j++) + sum+=tr[j]; + atr_buffer[i]=sum/m_atr_period; + } + else + if(i > m_atr_period) + atr_buffer[i] = (atr_buffer[i-1]*(m_atr_period-1)+tr[i])/m_atr_period; + + //--- STEP 3: Calculate Middle Line (MA) + if(i >= m_ma_period - 1) + { + switch(m_ma_method) + { + case MODE_EMA: + case MODE_SMMA: + if(i==m_ma_period-1) + { + double sum=0; + for(int j=0; j0) middle_buffer[i]=sum/w_sum;} + break; + default: + {double sum=0; for(int j=0; j= start_pos) + { + upper_buffer[i] = middle_buffer[i] + (atr_buffer[i] * m_multiplier); + lower_buffer[i] = middle_buffer[i] - (atr_buffer[i] * m_multiplier); + } + } + } + +//+------------------------------------------------------------------+ +//| CKeltnerChannelCalculator: Prepares the standard MA source price.| +//+------------------------------------------------------------------+ +bool CKeltnerChannelCalculator::PrepareMAPriceSeries(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], ENUM_APPLIED_PRICE price_type) + { + ArrayResize(m_ma_price, rates_total); + switch(price_type) + { + case PRICE_OPEN: + ArrayCopy(m_ma_price, open, 0, 0, rates_total); + break; + case PRICE_HIGH: + ArrayCopy(m_ma_price, high, 0, 0, rates_total); + break; + case PRICE_LOW: + ArrayCopy(m_ma_price, low, 0, 0, rates_total); + break; + case PRICE_MEDIAN: + for(int i=0; i