diff --git a/Include/MyIncludes/Laguerre_Filter_Adaptive_Calculator.mqh b/Include/MyIncludes/Laguerre_Filter_Adaptive_Calculator.mqh new file mode 100644 index 0000000..ef70c78 --- /dev/null +++ b/Include/MyIncludes/Laguerre_Filter_Adaptive_Calculator.mqh @@ -0,0 +1,252 @@ +//+------------------------------------------------------------------+ +//| Laguerre_Filter_Adaptive_Calculator.mqh | +//| Calculation engine for the Adaptive Laguerre Filter. | +//| Copyright 2025, xxxxxxxx | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2025, xxxxxxxx" + +#include + +//+==================================================================+ +//| | +//| CLASS 1: CLaguerreFilterAdaptiveCalculator (Base) | +//| | +//+==================================================================+ +class CLaguerreFilterAdaptiveCalculator + { +protected: + 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: + CLaguerreFilterAdaptiveCalculator(void) {}; + virtual ~CLaguerreFilterAdaptiveCalculator(void) {}; + + bool Init(void); + void Calculate(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], double &filter_buffer[]); + }; + +//+------------------------------------------------------------------+ +//| CLaguerreFilterAdaptiveCalculator: Initialization | +//+------------------------------------------------------------------+ +bool CLaguerreFilterAdaptiveCalculator::Init(void) + { + return true; + } + +//+------------------------------------------------------------------+ +//| CLaguerreFilterAdaptiveCalculator: Main Calculation Method | +//+------------------------------------------------------------------+ +void CLaguerreFilterAdaptiveCalculator::Calculate(int rates_total, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], double &filter_buffer[]) + { + if(rates_total < 10) // Need a few bars to warm up + return; + if(!PreparePriceSeries(rates_total, price_type, open, high, low, close)) + return; + +// --- Internal buffer for the band-pass filter results --- + double filt_buffer[]; + ArrayResize(filt_buffer, rates_total); + ArrayInitialize(filt_buffer, 0.0); + +// --- Cycle measurement (Homodyne Discriminator) variables --- + double Filt=0, Filt_prev=0, Filt_prev2=0; + double I1=0, Q1=0, I1_prev=0, Q1_prev=0; + double I2=0, Q2=0, I2_prev=0, Q2_prev=0; + double Re=0, Im=0; + double Period=0, Period_prev=0; + double DC_Period=0, DC_Period_prev=0; + +// --- Laguerre filter variables --- + double L0=0, L1=0, L2=0, L3=0; + double L0_prev=0, L1_prev=0, L2_prev=0, L3_prev=0; + +// --- Constants for band-pass filter --- + double alpha1 = (cos(0.707 * 2 * M_PI / 48.0) + sin(0.707 * 2 * M_PI / 48.0) - 1.0) / cos(0.707 * 2 * M_PI / 48.0); + double beta1 = 1.0 - alpha1 / 2.0; + beta1 *= beta1; + +// --- Full recalculation loop --- + for(int i = 0; i < rates_total; i++) + { + // --- Step 1: Band-Pass Filter to isolate cycle components --- + if(i > 1) + { + Filt = beta1 * (m_price[i] - 2 * m_price[i-1] + m_price[i-2]) + (2 * (1 - alpha1 / 2.0)) * Filt_prev - ((1 - alpha1 / 2.0) * (1 - alpha1 / 2.0)) * Filt_prev2; + } + else + { + Filt = 0; + } + filt_buffer[i] = Filt; + + // --- Step 2: Hilbert Transform to get InPhase and Quadrature components --- + if(i > 6) + { + Q1 = (0.0962 * filt_buffer[i] + 0.5769 * filt_buffer[i-2] - 0.5769 * filt_buffer[i-4] - 0.0962 * filt_buffer[i-6]) * (0.5 + 0.08 * (I1_prev + 50)); + I1 = filt_buffer[i-3]; + } + + // --- Step 3: Homodyne Discriminator to find phase --- + if(i > 0) + { + I2 = I1 - Q1_prev; + Q2 = Q1 + I1_prev; + Re = I2 * I2_prev + Q2 * Q2_prev; + Im = I2 * Q2_prev - Q2 * I2_prev; + } + + if(Im != 0.0 && Re != 0.0) + Period = 2 * M_PI / atan(Im / Re); + else + Period = 0.0; + + // --- Step 4: Clean up and smooth the calculated Period --- + if(Period > 1.5 * Period_prev) + Period = 1.5 * Period_prev; + if(Period < 0.67 * Period_prev) + Period = 0.67 * Period_prev; + if(Period < 6) + Period = 6; + if(Period > 50) + Period = 50; + DC_Period = 0.2 * Period + 0.8 * DC_Period_prev; + + // --- Step 5: Calculate the adaptive gamma for this bar --- + double gamma = 0.0; + if(DC_Period > 0) + gamma = 4.0 / DC_Period; + + // --- Step 6: Apply the Laguerre Filter with the dynamic gamma --- + if(i > 0) + { + L0 = (1.0 - gamma) * m_price[i] + gamma * L0_prev; + L1 = -gamma * L0 + L0_prev + gamma * L1_prev; + L2 = -gamma * L1 + L1_prev + gamma * L2_prev; + L3 = -gamma * L2 + L2_prev + gamma * L3_prev; + } + else // Initialization + { + L0 = m_price[i]; + L1 = m_price[i]; + L2 = m_price[i]; + L3 = m_price[i]; + } + + // --- CORRECTED: Calculate the final weighted filter output, not just L0 --- + filter_buffer[i] = (L0 + 2.0 * L1 + 2.0 * L2 + L3) / 6.0; + + // --- Update previous values for the next iteration --- + Filt_prev2 = Filt_prev; + Filt_prev = Filt; + I1_prev = I1; + Q1_prev = Q1; + I2_prev = I2; + Q2_prev = Q2; + Period_prev = Period; + DC_Period_prev = DC_Period; + L0_prev = L0; + L1_prev = L1; + L2_prev = L2; + L3_prev = L3; + } + } + +//+------------------------------------------------------------------+ +//| CLaguerreFilterAdaptiveCalculator: Prepares the standard source price. | +//+------------------------------------------------------------------+ +bool CLaguerreFilterAdaptiveCalculator::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