diff --git a/Include/MyIncludes/LinearRegression_Calculator.mqh b/Include/MyIncludes/LinearRegression_Calculator.mqh index 9c9890a..9f4c1f3 100644 --- a/Include/MyIncludes/LinearRegression_Calculator.mqh +++ b/Include/MyIncludes/LinearRegression_Calculator.mqh @@ -1,21 +1,20 @@ //+------------------------------------------------------------------+ //| LinearRegression_Calculator.mqh | -//| VERSION 2.00: Optimized for incremental calculation. | +//| VERSION 3.10: Supports both Moving and Static Channels. | //| Copyright 2025, xxxxxxxx | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, xxxxxxxx" #include -//--- Enum for Channel Calculation Mode --- enum ENUM_CHANNEL_MODE { - DEVIATION_STANDARD, // Channel width based on Standard Deviation - DEVIATION_MAXIMUM // Channel width based on Maximum Deviation + DEVIATION_STANDARD, + DEVIATION_MAXIMUM }; //+==================================================================+ -//| CLASS 1: CLinearRegressionCalculator (Base Class) | +//| CLASS: CLinearRegressionCalculator | //+==================================================================+ class CLinearRegressionCalculator { @@ -24,10 +23,9 @@ protected: ENUM_CHANNEL_MODE m_channel_mode; double m_deviations; - //--- Persistent Buffer for Incremental Calculation + //--- Persistent Buffer double m_price[]; - //--- 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: @@ -36,9 +34,13 @@ public: bool Init(int period, ENUM_CHANNEL_MODE mode, double deviations); - //--- Updated: Accepts prev_calculated - void Calculate(int rates_total, int prev_calculated, 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[]); + //--- Method 1: Moving Regression (The "Wavy" line) + void CalculateMoving(int rates_total, int prev_calculated, 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[]); + + //--- Method 2: Static Channel (The "Straight" segment for current bars) + void CalculateStaticChannel(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[]); }; //+------------------------------------------------------------------+ @@ -53,93 +55,162 @@ bool CLinearRegressionCalculator::Init(int period, ENUM_CHANNEL_MODE mode, doubl } //+------------------------------------------------------------------+ -//| Main Calculation (Optimized) | +//| Method 1: Moving Regression (Wavy) | //+------------------------------------------------------------------+ -void CLinearRegressionCalculator::Calculate(int rates_total, int prev_calculated, const double &open[], const double &high[], const double &low[], const double &close[], ENUM_APPLIED_PRICE price_type, +void CLinearRegressionCalculator::CalculateMoving(int rates_total, int prev_calculated, 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[]) { 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_price) != rates_total) ArrayResize(m_price, rates_total); - -//--- 3. Prepare Price (Optimized) if(!PreparePriceSeries(rates_total, start_index, price_type, open, high, low, close)) return; -//--- 4. Calculate Linear Regression (Always recalculate for the window) - int regression_start_index = rates_total - m_period; -// Calculate Sums - double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0; - for(int i = 0; i < m_period; i++) +// Pre-calc X sums + double sum_x = 0, sum_x2 = 0; + for(int k = 0; k < m_period; k++) { - double y = m_price[regression_start_index + i]; - double x = i; - sum_x += x; - sum_y += y; - sum_xy += x * y; - sum_x2 += x * x; + sum_x += k; + sum_x2 += k * k; } + double denominator = m_period * sum_x2 - sum_x * sum_x; - double b = (m_period * sum_xy - sum_x * sum_y) / (m_period * sum_x2 - sum_x * sum_x); - double a = (sum_y - b * sum_x) / m_period; + int loop_start = MathMax(m_period - 1, start_index); - double deviation_offset = 0; - double regression_values[]; - ArrayResize(regression_values, m_period); - - if(m_channel_mode == DEVIATION_STANDARD) + for(int i = loop_start; i < rates_total; i++) { - double dev_sum_sq = 0; - for(int i = 0; i < m_period; i++) + double sum_y = 0, sum_xy = 0; + for(int k = 0; k < m_period; k++) { - regression_values[i] = a + b * i; - dev_sum_sq += MathPow(m_price[regression_start_index + i] - regression_values[i], 2); + double price = m_price[i - m_period + 1 + k]; + sum_y += price; + sum_xy += k * price; } - deviation_offset = m_deviations * MathSqrt(dev_sum_sq / m_period); - } - else // DEVIATION_MAXIMUM - { - double max_dev = 0; - for(int i = 0; i < m_period; i++) - { - regression_values[i] = a + b * i; - max_dev = MathMax(max_dev, MathAbs(m_price[regression_start_index + i] - regression_values[i])); - } - deviation_offset = max_dev; - } -// Fill Buffers - for(int i = 0; i < m_period; i++) - { - int buffer_index = regression_start_index + i; - middle_buffer[buffer_index] = regression_values[i]; - upper_buffer[buffer_index] = regression_values[i] + deviation_offset; - lower_buffer[buffer_index] = regression_values[i] - deviation_offset; - } - if(regression_start_index > 0) - { - middle_buffer[regression_start_index-1] = EMPTY_VALUE; - upper_buffer[regression_start_index-1] = EMPTY_VALUE; - lower_buffer[regression_start_index-1] = EMPTY_VALUE; + double b = (m_period * sum_xy - sum_x * sum_y) / denominator; + double a = (sum_y - b * sum_x) / m_period; + double regression_value = a + b * (m_period - 1); // End point + + middle_buffer[i] = regression_value; + + double deviation_offset = 0; + if(m_channel_mode == DEVIATION_STANDARD) + { + double dev_sum_sq = 0; + for(int k = 0; k < m_period; k++) + { + double price = m_price[i - m_period + 1 + k]; + double reg_val_at_k = a + b * k; + dev_sum_sq += MathPow(price - reg_val_at_k, 2); + } + deviation_offset = m_deviations * MathSqrt(dev_sum_sq / m_period); + } + else + { + double max_dev = 0; + for(int k = 0; k < m_period; k++) + { + double price = m_price[i - m_period + 1 + k]; + double reg_val_at_k = a + b * k; + max_dev = MathMax(max_dev, MathAbs(price - reg_val_at_k)); + } + deviation_offset = max_dev; + } + + upper_buffer[i] = regression_value + deviation_offset; + lower_buffer[i] = regression_value - deviation_offset; } } //+------------------------------------------------------------------+ -//| Prepare Price (Standard - Optimized) | +//| Method 2: Static Channel (Straight) | +//+------------------------------------------------------------------+ +void CLinearRegressionCalculator::CalculateStaticChannel(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[]) + { + if(rates_total < m_period) + return; + +// Always update price buffer for the last segment + if(ArraySize(m_price) != rates_total) + ArrayResize(m_price, rates_total); + +// We only need to prepare the last 'm_period' prices + int start_prep = rates_total - m_period; + if(!PreparePriceSeries(rates_total, start_prep, price_type, open, high, low, close)) + return; + +// 1. Clear old history (Optimization: only clear if necessary, but for visual clarity we clear all before start) + if(start_prep > 0) + { + middle_buffer[start_prep - 1] = EMPTY_VALUE; + upper_buffer[start_prep - 1] = EMPTY_VALUE; + lower_buffer[start_prep - 1] = EMPTY_VALUE; + } + +// 2. Calculate Regression for the SINGLE window [rates_total-period ... rates_total-1] + double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0; + + for(int k = 0; k < m_period; k++) + { + double x = k; + double y = m_price[start_prep + k]; + sum_x += x; + sum_x2 += x * x; + sum_y += y; + sum_xy += x * y; + } + + double denominator = m_period * sum_x2 - sum_x * sum_x; + double b = (m_period * sum_xy - sum_x * sum_y) / denominator; + double a = (sum_y - b * sum_x) / m_period; + +// 3. Calculate Deviation + double deviation_offset = 0; + if(m_channel_mode == DEVIATION_STANDARD) + { + double dev_sum_sq = 0; + for(int k = 0; k < m_period; k++) + { + double y = m_price[start_prep + k]; + double reg_val = a + b * k; + dev_sum_sq += MathPow(y - reg_val, 2); + } + deviation_offset = m_deviations * MathSqrt(dev_sum_sq / m_period); + } + else + { + double max_dev = 0; + for(int k = 0; k < m_period; k++) + { + double y = m_price[start_prep + k]; + double reg_val = a + b * k; + max_dev = MathMax(max_dev, MathAbs(y - reg_val)); + } + deviation_offset = max_dev; + } + +// 4. Draw the Straight Line Segment + for(int k = 0; k < m_period; k++) + { + int bar_index = start_prep + k; + double reg_val = a + b * k; + + middle_buffer[bar_index] = reg_val; + upper_buffer[bar_index] = reg_val + deviation_offset; + lower_buffer[bar_index] = reg_val - deviation_offset; + } + } + +//+------------------------------------------------------------------+ +//| Prepare Price (Standard) | //+------------------------------------------------------------------+ bool CLinearRegressionCalculator::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) @@ -180,19 +251,16 @@ class CLinearRegressionCalculator_HA : public CLinearRegressionCalculator { private: CHeikinAshi_Calculator m_ha_calculator; - // Internal HA buffers double m_ha_open[], m_ha_high[], m_ha_low[], m_ha_close[]; - protected: 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[]) override; }; //+------------------------------------------------------------------+ -//| Prepare Price (Heikin Ashi - Optimized) | +//| | //+------------------------------------------------------------------+ bool CLinearRegressionCalculator_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); @@ -200,12 +268,7 @@ bool CLinearRegressionCalculator_HA::PreparePriceSeries(int rates_total, int sta ArrayResize(m_ha_low, rates_total); 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) + m_ha_calculator.Calculate(rates_total, start_index, open, high, low, close, m_ha_open, m_ha_high, m_ha_low, m_ha_close); for(int i = start_index; i < rates_total; i++) { switch(price_type)