diff --git a/Include/MyIncludes/Laguerre_Adaptive_Channel_Calculator.mqh b/Include/MyIncludes/Laguerre_Adaptive_Channel_Calculator.mqh new file mode 100644 index 0000000..7bfc5ba --- /dev/null +++ b/Include/MyIncludes/Laguerre_Adaptive_Channel_Calculator.mqh @@ -0,0 +1,280 @@ +//+------------------------------------------------------------------+ +//| Laguerre_Adaptive_Channel_Calculator.mqh | +//| Copyright 2026, xxxxxxxx| +//+------------------------------------------------------------------+ +#property copyright "Copyright 2026, xxxxxxxx" +#property version "1.10" // Upgraded with dedicated ENUM_CHANNEL_WIDTH_METHOD for strict type safety +#property description "Stateful calculator implementing volatility bands around Adaptive Laguerre Filter." + +#ifndef LAGUERRE_ADAPTIVE_CHANNEL_CALCULATOR_MQH +#define LAGUERRE_ADAPTIVE_CHANNEL_CALCULATOR_MQH + +#include +#include +#include + +//--- Dedicated Channel Volatility Width enum to prevent dimensionless metrics (like ER) from causing UI confusion +enum ENUM_CHANNEL_WIDTH_METHOD + { + WIDTH_METHOD_ATR, // Average True Range (ATR Keltner-style) + WIDTH_METHOD_STAND_DEV // Standard Deviation (StDev Bollinger-style) + }; + +//+==================================================================+ +//| CLASS: CLaguerreAdaptiveChannelCalculator | +//+==================================================================+ +class CLaguerreAdaptiveChannelCalculator + { +private: + ENUM_CHANNEL_WIDTH_METHOD m_width_method; + int m_width_period; + double m_multiplier; + bool m_is_ha; + + CLaguerreAdaptiveFilterCalculator *m_baseline_calc; + CATRCalculator *m_atr_calc; + + //--- Persistent State Registers + double m_baseline_buffer[]; + double m_vol_buffer[]; + double m_price[]; + + 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: + CLaguerreAdaptiveChannelCalculator(void); + ~CLaguerreAdaptiveChannelCalculator(void); + + bool Init(ENUM_ADAPTIVE_METHOD method, int adaptive_period, double gamma_min, double gamma_max, + ENUM_CHANNEL_WIDTH_METHOD width_method, int width_period, double multiplier, bool is_ha); + + 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 &baseline_buffer[], double &upper_buffer[], double &lower_buffer[]); + }; + +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CLaguerreAdaptiveChannelCalculator::CLaguerreAdaptiveChannelCalculator(void) + : m_width_method(WIDTH_METHOD_ATR), + m_width_period(10), + m_multiplier(2.0), + m_is_ha(false), + m_baseline_calc(NULL), + m_atr_calc(NULL) + { + } + +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CLaguerreAdaptiveChannelCalculator::~CLaguerreAdaptiveChannelCalculator(void) + { + if(CheckPointer(m_baseline_calc) != POINTER_INVALID) + delete m_baseline_calc; + if(CheckPointer(m_atr_calc) != POINTER_INVALID) + delete m_atr_calc; + } + +//+------------------------------------------------------------------+ +//| Init (Strict Type Safety Enforced) | +//+------------------------------------------------------------------+ +bool CLaguerreAdaptiveChannelCalculator::Init(ENUM_ADAPTIVE_METHOD method, int adaptive_period, double gamma_min, double gamma_max, + ENUM_CHANNEL_WIDTH_METHOD width_method, int width_period, double multiplier, bool is_ha) + { + m_width_method = width_method; + m_width_period = (width_period < 2) ? 2 : width_period; + m_multiplier = (multiplier <= 0.0) ? 1.0 : multiplier; + m_is_ha = is_ha; + + if(CheckPointer(m_baseline_calc) != POINTER_INVALID) + { + delete m_baseline_calc; + m_baseline_calc = NULL; + } + if(CheckPointer(m_atr_calc) != POINTER_INVALID) + { + delete m_atr_calc; + m_atr_calc = NULL; + } + +// Instantiate Baseline Calculator + m_baseline_calc = new CLaguerreAdaptiveFilterCalculator(); + if(CheckPointer(m_baseline_calc) == POINTER_INVALID || + !m_baseline_calc.Init(method, adaptive_period, gamma_min, gamma_max, m_is_ha)) + return false; + +// Instantiate ATR Width Calculator if selected + if(m_width_method == WIDTH_METHOD_ATR) + { + if(m_is_ha) + m_atr_calc = new CATRCalculator_HA(); + else + m_atr_calc = new CATRCalculator(); + + if(CheckPointer(m_atr_calc) == POINTER_INVALID || !m_atr_calc.Init(m_width_period, ATR_POINTS)) + return false; + } + + return true; + } + +//+------------------------------------------------------------------+ +//| Calculate | +//+------------------------------------------------------------------+ +void CLaguerreAdaptiveChannelCalculator::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 &baseline_buffer[], double &upper_buffer[], double &lower_buffer[]) + { + int required_bars = MathMax(m_width_period * 2, 20) + 5; + if(rates_total < required_bars) + return; + +//--- Resize state buffers and enforce chronological safety + if(ArraySize(m_baseline_buffer) != rates_total) + { + ArrayResize(m_baseline_buffer, rates_total); + ArrayResize(m_vol_buffer, rates_total); + ArrayResize(m_price, rates_total); + + ArraySetAsSeries(m_baseline_buffer, false); + ArraySetAsSeries(m_vol_buffer, false); + ArraySetAsSeries(m_price, false); + } + +//--- 1. Calculate Adaptive Baseline (Filter Mean) + m_baseline_calc.Calculate(rates_total, prev_calculated, price_type, open, high, low, close, m_baseline_buffer); + +//--- 2. Calculate Channel Volatility Width (ATR or Standard Deviation) + if(m_width_method == WIDTH_METHOD_ATR) + { + // Refactored CATRCalculator v3.00 call + m_atr_calc.Calculate(rates_total, prev_calculated, open, high, low, close, m_vol_buffer); + } + else // WIDTH_METHOD_STAND_DEV (Bollinger Band Style volatility width) + { + int start_index = (prev_calculated > 0) ? prev_calculated - 1 : 0; + if(!PreparePriceSeries(rates_total, start_index, price_type, open, high, low, close)) + return; + + int loop_start = MathMax(m_width_period - 1, start_index); + if(loop_start == m_width_period - 1) + { + for(int i = 0; i < loop_start; i++) + m_vol_buffer[i] = 0.0; + } + + for(int i = loop_start; i < rates_total; i++) + { + double sum = 0.0; + for(int j = 0; j < m_width_period; j++) + sum += m_price[i - j]; + double mean = sum / m_width_period; + + double sum_sq = 0.0; + for(int j = 0; j < m_width_period; j++) + sum_sq += pow(m_price[i - j] - mean, 2); + + m_vol_buffer[i] = sqrt(sum_sq / m_width_period); + } + } + +//--- 3. Calculate Upper and Lower bands around Baseline + int start = (prev_calculated > 0) ? prev_calculated - 1 : 0; + for(int i = start; i < rates_total; i++) + { + baseline_buffer[i] = m_baseline_buffer[i]; + upper_buffer[i] = m_baseline_buffer[i] + m_multiplier * m_vol_buffer[i]; + lower_buffer[i] = m_baseline_buffer[i] - m_multiplier * m_vol_buffer[i]; + } + } + +//+------------------------------------------------------------------+ +//| Prepare Price Series | +//+------------------------------------------------------------------+ +bool CLaguerreAdaptiveChannelCalculator::PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, + const double &open[], const double &high[], const double &low[], const double &close[]) + { + if(m_is_ha) + { + static CHeikinAshi_Calculator ha_calc; + static double ha_open[], ha_high[], ha_low[], ha_close[]; + if(ArraySize(ha_open) != rates_total) + { + ArrayResize(ha_open, rates_total); + ArrayResize(ha_high, rates_total); + ArrayResize(ha_low, rates_total); + ArrayResize(ha_close, rates_total); + ArraySetAsSeries(ha_open, false); + ArraySetAsSeries(ha_high, false); + ArraySetAsSeries(ha_low, false); + ArraySetAsSeries(ha_close, false); + } + + ha_calc.Calculate(rates_total, start_index, open, high, low, close, ha_open, ha_high, ha_low, ha_close); + + for(int i = start_index; i < rates_total; i++) + { + switch(price_type) + { + case PRICE_OPEN: + m_price[i] = ha_open[i]; + break; + case PRICE_HIGH: + m_price[i] = ha_high[i]; + break; + case PRICE_LOW: + m_price[i] = ha_low[i]; + break; + case PRICE_MEDIAN: + m_price[i] = (ha_high[i] + ha_low[i]) * 0.5; + break; + case PRICE_TYPICAL: + m_price[i] = (ha_high[i] + ha_low[i] + ha_close[i]) / 3.0; + break; + case PRICE_WEIGHTED: + m_price[i] = (ha_high[i] + ha_low[i] + ha_close[i] * 2.0) * 0.25; + break; + default: + m_price[i] = ha_close[i]; + break; + } + } + } + else + { + for(int i = start_index; i < rates_total; i++) + { + switch(price_type) + { + case PRICE_OPEN: + m_price[i] = open[i]; + break; + case PRICE_HIGH: + m_price[i] = high[i]; + break; + case PRICE_LOW: + m_price[i] = low[i]; + break; + case PRICE_MEDIAN: + m_price[i] = (high[i] + low[i]) * 0.5; + break; + case PRICE_TYPICAL: + m_price[i] = (high[i] + low[i] + close[i]) / 3.0; + break; + case PRICE_WEIGHTED: + m_price[i] = (high[i] + low[i] + close[i] * 2.0) * 0.25; + break; + default: + m_price[i] = close[i]; + break; + } + } + } + return true; + } + +#endif // LAGUERRE_ADAPTIVE_CHANNEL_CALCULATOR_MQH +//+------------------------------------------------------------------+