diff --git a/Include/MyIncludes/Bollinger_Bands_Calculator.mqh b/Include/MyIncludes/Bollinger_Bands_Calculator.mqh index 2b86995..08e64b3 100644 --- a/Include/MyIncludes/Bollinger_Bands_Calculator.mqh +++ b/Include/MyIncludes/Bollinger_Bands_Calculator.mqh @@ -1,10 +1,11 @@ //+------------------------------------------------------------------+ //| Bollinger_Bands_Calculator.mqh | -//| VERSION 2.00: Optimized for incremental calculation. | -//| Copyright 2025, xxxxxxxx | +//| VERSION 3.00: Refactored to use MovingAverage_Engine. | +//| Copyright 2026, xxxxxxxx | //+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" +#property copyright "Copyright 2026, xxxxxxxx" +#include #include //+==================================================================+ @@ -15,36 +16,59 @@ class CBollingerBandsCalculator protected: int m_period; double m_deviation; - ENUM_MA_METHOD m_ma_method; - //--- Persistent Buffers for Incremental Calculation + //--- Composition: Use Moving Average Engine + CMovingAverageCalculator *m_ma_engine; + + //--- Persistent Buffers double m_price[]; - double m_ma_buffer[]; + double m_ma_buffer[]; // Internal buffer for centerline //--- Updated: Accepts start_index virtual bool PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]); public: - CBollingerBandsCalculator(void) {}; - virtual ~CBollingerBandsCalculator(void) {}; + CBollingerBandsCalculator(void); + virtual ~CBollingerBandsCalculator(void); - bool Init(int period, double deviation, ENUM_MA_METHOD ma_method); + bool Init(int period, double deviation, ENUM_MA_TYPE ma_type); //--- Updated: Accepts prev_calculated void Calculate(int rates_total, int prev_calculated, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], double &ma_out[], double &upper_out[], double &lower_out[]); - //--- NEW: Accessor for internal price buffer (needed for %B) + void GetPriceBuffer(double &dest_array[]); }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CBollingerBandsCalculator::CBollingerBandsCalculator(void) + { + m_ma_engine = new CMovingAverageCalculator(); + } + +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CBollingerBandsCalculator::~CBollingerBandsCalculator(void) + { + if(CheckPointer(m_ma_engine) != POINTER_INVALID) + delete m_ma_engine; + } + //+------------------------------------------------------------------+ //| Init | //+------------------------------------------------------------------+ -bool CBollingerBandsCalculator::Init(int period, double deviation, ENUM_MA_METHOD ma_method) +bool CBollingerBandsCalculator::Init(int period, double deviation, ENUM_MA_TYPE ma_type) { m_period = (period < 1) ? 1 : period; m_deviation = deviation; - m_ma_method = ma_method; + +// Initialize the MA engine + if(!m_ma_engine.Init(m_period, ma_type)) + return false; + return true; } @@ -58,11 +82,7 @@ void CBollingerBandsCalculator::Calculate(int rates_total, int prev_calculated, return; //--- 1. Determine Start Index - int start_index; - if(prev_calculated == 0) - start_index = 0; - else - start_index = prev_calculated - 1; + int start_index = (prev_calculated == 0) ? 0 : prev_calculated - 1; //--- 2. Resize Buffers if(ArraySize(m_price) != rates_total) @@ -75,70 +95,32 @@ void CBollingerBandsCalculator::Calculate(int rates_total, int prev_calculated, if(!PreparePriceSeries(rates_total, start_index, price_type, open, high, low, close)) return; -//--- 4. Calculate Centerline (MA) - Incremental - int ma_start_pos = m_period - 1; - int loop_start = MathMax(ma_start_pos, start_index); - - for(int i = loop_start; i < rates_total; i++) - { - switch(m_ma_method) - { - case MODE_EMA: - case MODE_SMMA: - if(i == ma_start_pos) - { - double sum = 0; - for(int j = 0; j < m_period; j++) - sum += m_price[i-j]; - m_ma_buffer[i] = sum / m_period; - } - else - { - if(m_ma_method == MODE_EMA) - { - double pr = 2.0 / (m_period + 1.0); - m_ma_buffer[i] = m_price[i] * pr + m_ma_buffer[i-1] * (1.0 - pr); - } - else - m_ma_buffer[i] = (m_ma_buffer[i-1] * (m_period - 1) + m_price[i]) / m_period; - } - break; - case MODE_LWMA: - { - double lwma_sum = 0, weight_sum = 0; - for(int j = 0; j < m_period; j++) - { - int weight = m_period - j; - lwma_sum += m_price[i-j] * weight; - weight_sum += weight; - } - if(weight_sum > 0) - m_ma_buffer[i] = lwma_sum / weight_sum; - break; - } - default: // MODE_SMA - { - double sum = 0; - for(int j = 0; j < m_period; j++) - sum += m_price[i-j]; - m_ma_buffer[i] = sum / m_period; - break; - } - } - } +//--- 4. Calculate Centerline (Using Engine on Custom Array) +// We use CalculateOnArray because we have already prepared m_price (which handles HA logic if needed) + m_ma_engine.CalculateOnArray(rates_total, prev_calculated, m_price, m_ma_buffer); //--- 5. Calculate Bands (Incremental) + int loop_start = MathMax(m_period - 1, start_index); + for(int i = loop_start; i < rates_total; i++) { - double std_dev_val = 0, sum_sq = 0; + double sum_sq = 0; + // Standard Deviation Calculation + // Note: Standard Bollinger Bands use the SMA of (Price - MA)^2 if the center line is SMA. + // If the center line is EMA, usually the StdDev is still calculated over the raw period window. for(int j = 0; j < m_period; j++) - sum_sq += pow(m_price[i-j] - m_ma_buffer[i], 2); - std_dev_val = sqrt(sum_sq / m_period); + { + double diff = m_price[i-j] - m_ma_buffer[i]; + sum_sq += diff * diff; + } + + double std_dev_val = sqrt(sum_sq / m_period); upper_out[i] = m_ma_buffer[i] + m_deviation * std_dev_val; lower_out[i] = m_ma_buffer[i] - m_deviation * std_dev_val; } +// Copy internal MA buffer to output ArrayCopy(ma_out, m_ma_buffer, 0, 0, rates_total); } @@ -147,7 +129,6 @@ void CBollingerBandsCalculator::Calculate(int rates_total, int prev_calculated, //+------------------------------------------------------------------+ bool CBollingerBandsCalculator::PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { -// Optimized copy loop for(int i = start_index; i < rates_total; i++) { switch(price_type) @@ -165,13 +146,13 @@ bool CBollingerBandsCalculator::PreparePriceSeries(int rates_total, int start_in m_price[i] = low[i]; break; case PRICE_MEDIAN: - m_price[i] = (high[i]+low[i])/2.0; + m_price[i] = (high[i] + low[i]) / 2.0; break; case PRICE_TYPICAL: - m_price[i] = (high[i]+low[i]+close[i])/3.0; + m_price[i] = (high[i] + low[i] + close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_price[i] = (high[i]+low[i]+close[i]+close[i])/4.0; + m_price[i] = (high[i] + low[i] + 2 * close[i]) / 4.0; break; default: m_price[i] = close[i]; @@ -188,7 +169,6 @@ class CBollingerBandsCalculator_HA : public CBollingerBandsCalculator { private: CHeikinAshi_Calculator m_ha_calculator; - // Internal HA buffers double m_ha_open[], m_ha_high[], m_ha_low[], m_ha_close[]; protected: @@ -200,7 +180,6 @@ protected: //+------------------------------------------------------------------+ bool CBollingerBandsCalculator_HA::PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { -// Resize internal HA buffers if(ArraySize(m_ha_open) != rates_total) { ArrayResize(m_ha_open, rates_total); @@ -209,11 +188,9 @@ bool CBollingerBandsCalculator_HA::PreparePriceSeries(int rates_total, int start ArrayResize(m_ha_close, rates_total); } -//--- STRICT CALL: Use the optimized 10-param HA calculation m_ha_calculator.Calculate(rates_total, start_index, open, high, low, close, m_ha_open, m_ha_high, m_ha_low, m_ha_close); -//--- Copy to m_price (Optimized loop) for(int i = start_index; i < rates_total; i++) { switch(price_type) @@ -231,13 +208,13 @@ bool CBollingerBandsCalculator_HA::PreparePriceSeries(int rates_total, int start m_price[i] = m_ha_low[i]; break; case PRICE_MEDIAN: - m_price[i] = (m_ha_high[i]+m_ha_low[i])/2.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i]) / 2.0; break; case PRICE_TYPICAL: - m_price[i] = (m_ha_high[i]+m_ha_low[i]+m_ha_close[i])/3.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i] + m_ha_close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_price[i] = (m_ha_high[i]+m_ha_low[i]+2*m_ha_close[i])/4.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i] + 2 * m_ha_close[i]) / 4.0; break; default: m_price[i] = m_ha_close[i];