diff --git a/Include/MyIncludes/TSI_Calculator.mqh b/Include/MyIncludes/TSI_Calculator.mqh index 327adfc..779ad4f 100644 --- a/Include/MyIncludes/TSI_Calculator.mqh +++ b/Include/MyIncludes/TSI_Calculator.mqh @@ -1,9 +1,9 @@ //+------------------------------------------------------------------+ //| TSI_Calculator.mqh | -//| VERSION 4.10: Added missing Getters. | -//| Copyright 2025, xxxxxxxx | +//| VERSION 5.00: Unified calculator for TSI and Oscillator. | +//| Copyright 2026, xxxxxxxx | //+------------------------------------------------------------------+ -#property copyright "Copyright 2025, xxxxxxxx" +#property copyright "Copyright 2026, xxxxxxxx" #include #include @@ -31,19 +31,27 @@ protected: double m_ema1_mtm[], m_ema1_abs[]; // First Smoothing double m_ema2_mtm[], m_ema2_abs[]; // Second Smoothing + //--- Internal Result Buffers + double m_tsi_internal[]; + double m_signal_internal[]; + double m_osc_internal[]; + 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: CTSICalculator(void); virtual ~CTSICalculator(void); - //--- Init now takes MA types for core calculation too bool Init(int slow_p, ENUM_MA_TYPE slow_ma, int fast_p, ENUM_MA_TYPE fast_ma, int signal_p, ENUM_MA_TYPE signal_ma); + //--- Main Calculation 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 &tsi_buffer[], double &signal_buffer[]); + double &tsi_out[], double &signal_out[], double &osc_out[]); + + //--- Wrapper for Oscillator Only + void CalculateOscillatorOnly(int rates_total, int prev_calculated, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], + double &osc_out[]); - //--- ADDED: Missing Getters required by Oscillator wrapper int GetPeriodSlow() const { return m_slow_p; } int GetPeriodFast() const { return m_fast_p; } int GetPeriodSignal() const { return m_signal_p; } @@ -72,19 +80,14 @@ bool CTSICalculator::Init(int slow_p, ENUM_MA_TYPE slow_ma, int fast_p, ENUM_MA_ m_fast_p = (fast_p < 1) ? 1 : fast_p; m_signal_p = (signal_p < 1) ? 1 : signal_p; -// Initialize Core Engines (Momentum) if(!m_slow_mtm_engine.Init(m_slow_p, slow_ma)) return false; if(!m_fast_mtm_engine.Init(m_fast_p, fast_ma)) return false; - -// Initialize Core Engines (Abs Momentum) if(!m_slow_abs_engine.Init(m_slow_p, slow_ma)) return false; if(!m_fast_abs_engine.Init(m_fast_p, fast_ma)) return false; - -// Initialize Signal Engine if(!m_signal_ma_engine.Init(m_signal_p, signal_ma)) return false; @@ -95,9 +98,8 @@ bool CTSICalculator::Init(int slow_p, ENUM_MA_TYPE slow_ma, int fast_p, ENUM_MA_ //| Main Calculation | //+------------------------------------------------------------------+ void CTSICalculator::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 &tsi_buffer[], double &signal_buffer[]) + double &tsi_out[], double &signal_out[], double &osc_out[]) { -// Minimum bars check if(rates_total <= m_slow_p + m_fast_p + m_signal_p) return; @@ -113,12 +115,15 @@ void CTSICalculator::Calculate(int rates_total, int prev_calculated, ENUM_APPLIE ArrayResize(m_ema1_abs, rates_total); ArrayResize(m_ema2_mtm, rates_total); ArrayResize(m_ema2_abs, rates_total); + ArrayResize(m_tsi_internal, rates_total); + ArrayResize(m_signal_internal, rates_total); + ArrayResize(m_osc_internal, rates_total); } if(!PreparePriceSeries(rates_total, start_index, price_type, open, high, low, close)) return; -//--- 1. Calculate Momentum (Raw) +// 1. Calculate Momentum (Raw) int loop_start = MathMax(1, start_index); if(loop_start == 1) { @@ -133,41 +138,59 @@ void CTSICalculator::Calculate(int rates_total, int prev_calculated, ENUM_APPLIE m_abs_mtm[i] = MathAbs(diff); } -//--- 2. First Smoothing (Slow MA) -// Input: m_mtm / m_abs_mtm -// Offset: 1 (because momentum starts at index 1) +// 2. First Smoothing (Slow MA) m_slow_mtm_engine.CalculateOnArray(rates_total, prev_calculated, m_mtm, m_ema1_mtm, 1); m_slow_abs_engine.CalculateOnArray(rates_total, prev_calculated, m_abs_mtm, m_ema1_abs, 1); -//--- 3. Second Smoothing (Fast MA) -// Input: m_ema1_mtm / m_ema1_abs -// Offset: 1 + slow_period - 1 = slow_period +// 3. Second Smoothing (Fast MA) int offset2 = m_slow_p; m_fast_mtm_engine.CalculateOnArray(rates_total, prev_calculated, m_ema1_mtm, m_ema2_mtm, offset2); m_fast_abs_engine.CalculateOnArray(rates_total, prev_calculated, m_ema1_abs, m_ema2_abs, offset2); -//--- 4. Calculate TSI -// Valid from: offset2 + fast_period - 1 = slow_period + fast_period - 1 +// 4. Calculate TSI int tsi_start = m_slow_p + m_fast_p - 1; int loop_start_tsi = MathMax(tsi_start, start_index); - if(prev_calculated == 0) - ArrayInitialize(tsi_buffer, 0.0); - for(int i = loop_start_tsi; i < rates_total; i++) { if(m_ema2_abs[i] > 0.0000001) - tsi_buffer[i] = 100.0 * (m_ema2_mtm[i] / m_ema2_abs[i]); + m_tsi_internal[i] = 100.0 * (m_ema2_mtm[i] / m_ema2_abs[i]); else - tsi_buffer[i] = 0.0; + m_tsi_internal[i] = 0.0; } -//--- 5. Calculate Signal Line - m_signal_ma_engine.CalculateOnArray(rates_total, prev_calculated, tsi_buffer, signal_buffer, tsi_start); +// 5. Calculate Signal Line + m_signal_ma_engine.CalculateOnArray(rates_total, prev_calculated, m_tsi_internal, m_signal_internal, tsi_start); + +// 6. Calculate Oscillator & Output + int osc_start = tsi_start + m_signal_p - 1; + int loop_start_osc = MathMax(osc_start, start_index); + + for(int i = loop_start_osc; i < rates_total; i++) + { + m_osc_internal[i] = m_tsi_internal[i] - m_signal_internal[i]; + + if(ArraySize(tsi_out) == rates_total) + tsi_out[i] = m_tsi_internal[i]; + if(ArraySize(signal_out) == rates_total) + signal_out[i] = m_signal_internal[i]; + if(ArraySize(osc_out) == rates_total) + osc_out[i] = m_osc_internal[i]; + } } //+------------------------------------------------------------------+ -//| Prepare Price (Standard - Optimized) | +//| Calculate Oscillator Only | +//+------------------------------------------------------------------+ +void CTSICalculator::CalculateOscillatorOnly(int rates_total, int prev_calculated, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[], + double &osc_out[]) + { + double dummy_tsi[], dummy_signal[]; + Calculate(rates_total, prev_calculated, price_type, open, high, low, close, dummy_tsi, dummy_signal, osc_out); + } + +//+------------------------------------------------------------------+ +//| Prepare Price (Standard) | //+------------------------------------------------------------------+ bool CTSICalculator::PreparePriceSeries(int rates_total, int start_index, ENUM_APPLIED_PRICE price_type, const double &open[], const double &high[], const double &low[], const double &close[]) { @@ -188,13 +211,13 @@ bool CTSICalculator::PreparePriceSeries(int rates_total, int start_index, ENUM_A m_price[i] = low[i]; break; case PRICE_MEDIAN: - m_price[i] = (high[i]+low[i])/2.0; + m_price[i] = (high[i] + low[i]) / 2.0; break; case PRICE_TYPICAL: - m_price[i] = (high[i]+low[i]+close[i])/3.0; + m_price[i] = (high[i] + low[i] + close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_price[i] = (high[i]+low[i]+2*close[i])/4.0; + m_price[i] = (high[i] + low[i] + 2 * close[i]) / 4.0; break; default: m_price[i] = close[i]; @@ -217,7 +240,7 @@ protected: }; //+------------------------------------------------------------------+ -//| | +//| Prepare Price (Heikin Ashi) | //+------------------------------------------------------------------+ bool CTSICalculator_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[]) { @@ -246,13 +269,13 @@ bool CTSICalculator_HA::PreparePriceSeries(int rates_total, int start_index, ENU m_price[i] = m_ha_low[i]; break; case PRICE_MEDIAN: - m_price[i] = (m_ha_high[i]+m_ha_low[i])/2.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i]) / 2.0; break; case PRICE_TYPICAL: - m_price[i] = (m_ha_high[i]+m_ha_low[i]+m_ha_close[i])/3.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i] + m_ha_close[i]) / 3.0; break; case PRICE_WEIGHTED: - m_price[i] = (m_ha_high[i]+m_ha_low[i]+2*m_ha_close[i])/4.0; + m_price[i] = (m_ha_high[i] + m_ha_low[i] + 2 * m_ha_close[i]) / 4.0; break; default: m_price[i] = m_ha_close[i];