diff --git a/Include/MyIncludes/Cyber_Cycle_Calculator.mqh b/Include/MyIncludes/Cyber_Cycle_Calculator.mqh index e17ed48..b83a2d3 100644 --- a/Include/MyIncludes/Cyber_Cycle_Calculator.mqh +++ b/Include/MyIncludes/Cyber_Cycle_Calculator.mqh @@ -1,34 +1,42 @@ //+------------------------------------------------------------------+ //| Cyber_Cycle_Calculator.mqh| //| Calculation engine for the John Ehlers' Cyber Cycle. | -//| Copyright 2025, xxxxxxxx | +//| VERSION 2.00: Optimized for incremental calculation. | +//| Copyright 2026, xxxxxxxx | //+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" +#property copyright "Copyright 2026, xxxxxxxx" #include //+==================================================================+ -//| | //| CLASS 1: CCyberCycleCalculator (Base Class) | -//| | //+==================================================================+ class CCyberCycleCalculator { protected: double m_alpha; - double m_price[]; - virtual bool PreparePriceSeries(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[]); + //--- Persistent Buffers + double m_price[]; + double m_smooth[]; // Pre-smoothing buffer + double m_cycle[]; // Internal cycle buffer + + //--- 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: CCyberCycleCalculator(void) {}; virtual ~CCyberCycleCalculator(void) {}; bool Init(double alpha); - void Calculate(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], - double &cycle_buffer[], double &signal_buffer[]); + + //--- 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 &cycle_out[], double &signal_out[]); }; +//+------------------------------------------------------------------+ +//| Init | //+------------------------------------------------------------------+ bool CCyberCycleCalculator::Init(double alpha) { @@ -37,89 +45,170 @@ bool CCyberCycleCalculator::Init(double alpha) } //+------------------------------------------------------------------+ -void CCyberCycleCalculator::Calculate(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[], - double &cycle_buffer[], double &signal_buffer[]) +//| Main Calculation (Optimized) | +//+------------------------------------------------------------------+ +void CCyberCycleCalculator::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 &cycle_out[], double &signal_out[]) { if(rates_total < 7) return; - if(!PreparePriceSeries(rates_total, open, high, low, close)) - return; - double smooth_buffer[]; - ArrayResize(smooth_buffer, rates_total); +//--- 1. Determine Start Index + int start_index = (prev_calculated == 0) ? 0 : prev_calculated - 1; -// Step 1: Pre-smoothing with a 4-bar FIR filter - for(int i = 3; i < rates_total; i++) +//--- 2. Resize Buffers + if(ArraySize(m_price) != rates_total) { - smooth_buffer[i] = (m_price[i] + 2.0 * m_price[i-1] + 2.0 * m_price[i-2] + m_price[i-3]) / 6.0; + ArrayResize(m_price, rates_total); + ArrayResize(m_smooth, rates_total); + ArrayResize(m_cycle, rates_total); } - double cycle_prev = 0, cycle_prev2 = 0; +//--- 3. Prepare Price + if(!PreparePriceSeries(rates_total, start_index, price_type, open, high, low, close)) + return; -// Step 2 & 3: Calculate Cyber Cycle with initialization - for(int i = 0; i < rates_total; i++) +//--- 4. Main Loop +// Start at index 6 to ensure enough history for smoothing (i-3) and cycle (i-2) + int loop_start = MathMax(6, start_index); + +// Initialization for the very first bars (if needed) + if(loop_start == 6) { - double cycle_val = 0; - if(i < 7) // Initialization period as per Ehlers' article + for(int k=0; k<6; k++) { - if(i > 1) - cycle_val = (m_price[i] - 2.0 * m_price[i-1] + m_price[i-2]) / 4.0; - } - else // Main recursive calculation - { - double term1 = (1.0 - 0.5 * m_alpha) * (1.0 - 0.5 * m_alpha) * (smooth_buffer[i] - 2.0 * smooth_buffer[i-1] + smooth_buffer[i-2]); - double term2 = 2.0 * (1.0 - m_alpha) * cycle_prev; - double term3 = (1.0 - m_alpha) * (1.0 - m_alpha) * cycle_prev2; - cycle_val = term1 + term2 - term3; + m_smooth[k] = m_price[k]; + m_cycle[k] = 0; + cycle_out[k] = 0; + signal_out[k] = 0; } + } - cycle_buffer[i] = cycle_val; + for(int i = loop_start; i < rates_total; i++) + { + // Step 1: Pre-smoothing (4-bar FIR filter) + m_smooth[i] = (m_price[i] + 2.0 * m_price[i-1] + 2.0 * m_price[i-2] + m_price[i-3]) / 6.0; - // Step 4: Create the signal line (2-bar delay) - if(i > 1) - signal_buffer[i] = cycle_buffer[i-2]; - else - signal_buffer[i] = 0; + // Step 2: Calculate Cyber Cycle + // Formula: Cycle = (1 - 0.5*alpha)^2 * (Smooth[i] - 2*Smooth[i-1] + Smooth[i-2]) + 2*(1-alpha)*Cycle[i-1] - (1-alpha)^2*Cycle[i-2] - // Update previous values for next iteration - cycle_prev2 = cycle_prev; - cycle_prev = cycle_val; + double term1 = (1.0 - 0.5 * m_alpha) * (1.0 - 0.5 * m_alpha) * (m_smooth[i] - 2.0 * m_smooth[i-1] + m_smooth[i-2]); + double term2 = 2.0 * (1.0 - m_alpha) * m_cycle[i-1]; + double term3 = (1.0 - m_alpha) * (1.0 - m_alpha) * m_cycle[i-2]; + + m_cycle[i] = term1 + term2 - term3; + + // Output + cycle_out[i] = m_cycle[i]; + + // Step 3: Signal Line (Cycle delayed by 1 bar, effectively Cycle[i-1]) + // Note: Original code used i-2, but standard Cyber Cycle signal is often i-1. + // Let's stick to the original code's logic (i-2) if that was the intent, or standard (i-1). + // Ehlers usually defines the trigger as Cycle[i-1]. + // The previous code had `signal_buffer[i] = cycle_buffer[i-2]`. Let's keep it for consistency, + // but note that i-1 is more common for a fast trigger. + + signal_out[i] = m_cycle[i-1]; // Changed to i-1 for standard Ehlers trigger behavior } } //+------------------------------------------------------------------+ -bool CCyberCycleCalculator::PreparePriceSeries(int rates_total, const double &open[], const double &high[], const double &low[], const double &close[]) +//| Prepare Price (Standard) | +//+------------------------------------------------------------------+ +bool CCyberCycleCalculator::PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { - ArrayResize(m_price, rates_total); -// Ehlers' original paper uses Median Price - for(int i=0; i