From 05bcdbf41223c5d35e3bf77398eee58d70c21302 Mon Sep 17 00:00:00 2001 From: Toh4iem9 Date: Sun, 18 Jan 2026 19:21:26 +0100 Subject: [PATCH] refactor(indicators): Added CalculateOnArray support --- Include/MyIncludes/Windowed_MA_Calculator.mqh | 78 +++++++++---------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/Include/MyIncludes/Windowed_MA_Calculator.mqh b/Include/MyIncludes/Windowed_MA_Calculator.mqh index ad46473..4e1aadb 100644 --- a/Include/MyIncludes/Windowed_MA_Calculator.mqh +++ b/Include/MyIncludes/Windowed_MA_Calculator.mqh @@ -1,14 +1,14 @@ //+------------------------------------------------------------------+ //| Windowed_MA_Calculator.mqh | //| Calculation engine for Hann Windowed FIR filter. | -//| VERSION 2.00: Optimized for incremental calculation. | -//| Copyright 2025, xxxxxxxx | +//| VERSION 2.10: Added CalculateOnArray support. | +//| Copyright 2026, xxxxxxxx | //+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" +#property copyright "Copyright 2026, xxxxxxxx" #include -enum ENUM_INPUT_SOURCE { SOURCE_PRICE, SOURCE_MOMENTUM }; // Price or (Close-Open) +enum ENUM_INPUT_SOURCE { SOURCE_PRICE, SOURCE_MOMENTUM }; //+==================================================================+ //| CLASS 1: CWindowedMACalculator (Base Class) | @@ -19,14 +19,13 @@ protected: int m_period; ENUM_INPUT_SOURCE m_source_type; - //--- Persistent Buffer for Incremental Calculation + //--- Persistent Buffer for Incremental Calculation (OHLC mode) double m_source_data[]; //--- Pre-calculated Weights double m_weights[]; double m_weight_sum; - //--- Updated: Accepts start_index virtual bool PrepareSourceData(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]); public: @@ -35,8 +34,11 @@ public: bool Init(int period, ENUM_INPUT_SOURCE source_type); - //--- Updated: Accepts prev_calculated + //--- Standard Calculation (OHLC) 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 &output_buffer[]); + + //--- Calculation on Custom Array + void CalculateOnArray(int rates_total, int prev_calculated, const double &src_buffer[], double &output_buffer[]); }; //+------------------------------------------------------------------+ @@ -47,21 +49,14 @@ bool CWindowedMACalculator::Init(int period, ENUM_INPUT_SOURCE source_type) m_period = (period < 2) ? 2 : period; m_source_type = source_type; -// Pre-calculate Weights +// Pre-calculate Weights (Ehlers' Modified Hann) ArrayResize(m_weights, m_period); m_weight_sum = 0; for(int j = 0; j < m_period; j++) { - // FIX: Changed from Standard Hann to Ehlers' Modified Hann - // Standard: 0.5 * (1 - cos(2*pi*j / (N-1))) -> Edges are ZERO. - // Ehlers: 1.0 - cos(2*pi*(j+1) / (N+1)) -> Edges are NON-ZERO. - - // Ehlers uses 1-based indexing in formula (count), we use 0-based (j). - // So (count) becomes (j + 1.0). - + // Ehlers formula: 1 - cos(2*pi*(j+1) / (N+1)) double weight = 1.0 - cos(2.0 * M_PI * (j + 1.0) / (m_period + 1.0)); - m_weights[j] = weight; m_weight_sum += weight; } @@ -70,48 +65,50 @@ bool CWindowedMACalculator::Init(int period, ENUM_INPUT_SOURCE source_type) } //+------------------------------------------------------------------+ -//| Main Calculation (Optimized) | +//| Main Calculation (Wrapper for OHLC) | //+------------------------------------------------------------------+ void CWindowedMACalculator::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 &output_buffer[]) { if(rates_total < m_period) 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 Buffer if(ArraySize(m_source_data) != rates_total) ArrayResize(m_source_data, rates_total); -//--- 3. Prepare Source Data (Optimized) if(!PrepareSourceData(rates_total, start_index, price_type, open, high, low, close)) return; -//--- 4. Calculate Windowed MA (Incremental Loop) +// Delegate to generic array calculation + CalculateOnArray(rates_total, prev_calculated, m_source_data, output_buffer); + } + +//+------------------------------------------------------------------+ +//| Calculate On Array (Core Logic) | +//+------------------------------------------------------------------+ +void CWindowedMACalculator::CalculateOnArray(int rates_total, int prev_calculated, const double &src_buffer[], double &output_buffer[]) + { + if(rates_total < m_period) + return; + + int start_index = (prev_calculated == 0) ? 0 : prev_calculated - 1; int loop_start = MathMax(m_period - 1, start_index); for(int i = loop_start; i < rates_total; i++) { double sum = 0; - - // Convolution: Sum(Price[i-j] * Weight[j]) - // Optimization: Weights are pre-calculated + // Convolution: Sum(Src[i-j] * Weight[j]) for(int j = 0; j < m_period; j++) { - sum += m_source_data[i-j] * m_weights[j]; + sum += src_buffer[i-j] * m_weights[j]; } - output_buffer[i] = sum / m_weight_sum; } } //+------------------------------------------------------------------+ -//| Prepare Source Data (Standard - Optimized) | +//| Prepare Source Data (Standard) | //+------------------------------------------------------------------+ bool CWindowedMACalculator::PrepareSourceData(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { @@ -134,13 +131,13 @@ bool CWindowedMACalculator::PrepareSourceData(int rates_total, int start_index, m_source_data[i] = low[i]; break; case PRICE_MEDIAN: - m_source_data[i] = (high[i]+low[i])/2.0; + m_source_data[i] = (high[i] + low[i]) / 2.0; break; case PRICE_TYPICAL: - m_source_data[i] = (high[i]+low[i]+close[i])/3.0; + m_source_data[i] = (high[i] + low[i] + close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_source_data[i] = (high[i]+low[i]+2*close[i])/4.0; + m_source_data[i] = (high[i] + low[i] + 2 * close[i]) / 4.0; break; default: m_source_data[i] = close[i]; @@ -156,13 +153,12 @@ bool CWindowedMACalculator::PrepareSourceData(int rates_total, int start_index, } //+==================================================================+ -//| CLASS 2: CWindowedMACalculator_HA (Heikin Ashi) | +//| CLASS 2: CWindowedMACalculator_HA | //+==================================================================+ class CWindowedMACalculator_HA : public CWindowedMACalculator { private: CHeikinAshi_Calculator m_ha_calculator; - // Internal HA buffers double m_ha_open[], m_ha_high[], m_ha_low[], m_ha_close[]; protected: @@ -170,7 +166,7 @@ protected: }; //+------------------------------------------------------------------+ -//| Prepare Source Data (Heikin Ashi - Optimized) | +//| Prepare Source Data (Heikin Ashi) | //+------------------------------------------------------------------+ bool CWindowedMACalculator_HA::PrepareSourceData(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { @@ -204,13 +200,13 @@ bool CWindowedMACalculator_HA::PrepareSourceData(int rates_total, int start_inde m_source_data[i] = m_ha_low[i]; break; case PRICE_MEDIAN: - m_source_data[i] = (m_ha_high[i]+m_ha_low[i])/2.0; + m_source_data[i] = (m_ha_high[i] + m_ha_low[i]) / 2.0; break; case PRICE_TYPICAL: - m_source_data[i] = (m_ha_high[i]+m_ha_low[i]+m_ha_close[i])/3.0; + m_source_data[i] = (m_ha_high[i] + m_ha_low[i] + m_ha_close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_source_data[i] = (m_ha_high[i]+m_ha_low[i]+2*m_ha_close[i])/4.0; + m_source_data[i] = (m_ha_high[i] + m_ha_low[i] + 2 * m_ha_close[i]) / 4.0; break; default: m_source_data[i] = m_ha_close[i];