From 917341a5be165bd6972210670604539b2747ccfb Mon Sep 17 00:00:00 2001 From: Toh4iem9 Date: Wed, 26 Aug 2026 11:47:59 +0200 Subject: [PATCH] refactor: Overloaded initialization, bounds safety & Custom Session support --- Include/MyIncludes/VScore_Calculator.mqh | 144 +++++++++++++++++------ 1 file changed, 105 insertions(+), 39 deletions(-) diff --git a/Include/MyIncludes/VScore_Calculator.mqh b/Include/MyIncludes/VScore_Calculator.mqh index 51b2ddc1..f1bd19b7 100644 --- a/Include/MyIncludes/VScore_Calculator.mqh +++ b/Include/MyIncludes/VScore_Calculator.mqh @@ -1,18 +1,19 @@ //+------------------------------------------------------------------+ //| VScore_Calculator.mqh | +//| Engine for Statistical V-Score (VWAP Z-Score) Calculation. | //| Copyright 2026, xxxxxxxx| //+------------------------------------------------------------------+ #property copyright "Copyright 2026, xxxxxxxx" -#property version "2.20" // Upgraded with strict internal chronological sorting safeguards for all VWAP and pricing buffers +#property version "3.00" // Overloaded initialization, bounds safety & Custom Session support #ifndef VSCORE_CALCULATOR_MQH #define VSCORE_CALCULATOR_MQH #include -//+------------------------------------------------------------------+ +//+==================================================================+ //| Class CVScoreCalculator | -//+------------------------------------------------------------------+ +//+==================================================================+ class CVScoreCalculator { protected: @@ -23,14 +24,24 @@ protected: double m_vwap_buf[]; double m_vwap_odd[]; double m_vwap_even[]; + double m_price[]; public: CVScoreCalculator(); virtual ~CVScoreCalculator(); + //--- Legacy Signature (100% Backward Compatible) bool Init(int period, ENUM_VWAP_PERIOD vwap_reset); - void Calculate(int rates_total, int prev_calculated, + //--- Enhanced Pro Signature (Standard Periods) + bool Init(const int period, const ENUM_VWAP_PERIOD vwap_reset, const ENUM_APPLIED_VOLUME vol_type, + const int tz_shift_hours=0, const bool is_heikin_ashi=false, const int max_history_days=0); + + //--- Enhanced Pro Signature (Custom Session) + bool Init(const int period, const string custom_start, const string custom_end, const ENUM_APPLIED_VOLUME vol_type, + const int tz_shift_hours=0, const bool is_heikin_ashi=false, const int max_history_days=0); + + void Calculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], double &out_vscore[]); @@ -39,8 +50,12 @@ public: //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ -CVScoreCalculator::CVScoreCalculator() : m_vwap_calc(NULL) +CVScoreCalculator::CVScoreCalculator() : m_period(20), m_vwap_calc(NULL) { + ArraySetAsSeries(m_vwap_buf, false); + ArraySetAsSeries(m_vwap_odd, false); + ArraySetAsSeries(m_vwap_even, false); + ArraySetAsSeries(m_price, false); } //+------------------------------------------------------------------+ @@ -49,90 +64,141 @@ CVScoreCalculator::CVScoreCalculator() : m_vwap_calc(NULL) CVScoreCalculator::~CVScoreCalculator() { if(CheckPointer(m_vwap_calc) != POINTER_INVALID) + { delete m_vwap_calc; + m_vwap_calc = NULL; + } } //+------------------------------------------------------------------+ -//| Init | +//| Legacy Init (Preserves compatibility with all scripts) | //+------------------------------------------------------------------+ bool CVScoreCalculator::Init(int period, ENUM_VWAP_PERIOD vwap_reset) { - m_period = (period < 2) ? 2 : period; - - m_vwap_calc = new CVWAPCalculator(); - if(CheckPointer(m_vwap_calc) == POINTER_INVALID) - return false; - -// Dynamic volume type selection based on broker capability (Real Volume vs Tick Volume) long volume_limit = (long)SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_LIMIT); ENUM_APPLIED_VOLUME vol_type = (volume_limit > 0) ? VOLUME_REAL : VOLUME_TICK; - -// Init VWAP with optimal volume, enabled - if(!m_vwap_calc.Init(vwap_reset, vol_type, 0, true)) - return false; - - return true; + return Init(period, vwap_reset, vol_type, 0, false, 0); } //+------------------------------------------------------------------+ -//| Main Calculation (Strictly O(1) Optimized) | +//| Enhanced Init (Standard Periods) | //+------------------------------------------------------------------+ -void CVScoreCalculator::Calculate(int rates_total, int prev_calculated, +bool CVScoreCalculator::Init(const int period, const ENUM_VWAP_PERIOD vwap_reset, const ENUM_APPLIED_VOLUME vol_type, + const int tz_shift_hours, const bool is_heikin_ashi, const int max_history_days) + { + m_period = (period < 2) ? 2 : period; + + if(CheckPointer(m_vwap_calc) != POINTER_INVALID) + { + delete m_vwap_calc; + m_vwap_calc = NULL; + } + + if(is_heikin_ashi) + m_vwap_calc = new CVWAPCalculator_HA(); + else + m_vwap_calc = new CVWAPCalculator(); + + if(CheckPointer(m_vwap_calc) == POINTER_INVALID) + return false; + + return m_vwap_calc.Init(vwap_reset, vol_type, tz_shift_hours, true, max_history_days); + } + +//+------------------------------------------------------------------+ +//| Enhanced Init (Custom Session) | +//+------------------------------------------------------------------+ +bool CVScoreCalculator::Init(const int period, const string custom_start, const string custom_end, const ENUM_APPLIED_VOLUME vol_type, + const int tz_shift_hours, const bool is_heikin_ashi, const int max_history_days) + { + m_period = (period < 2) ? 2 : period; + + if(CheckPointer(m_vwap_calc) != POINTER_INVALID) + { + delete m_vwap_calc; + m_vwap_calc = NULL; + } + + if(is_heikin_ashi) + m_vwap_calc = new CVWAPCalculator_HA(); + else + m_vwap_calc = new CVWAPCalculator(); + + if(CheckPointer(m_vwap_calc) == POINTER_INVALID) + return false; + + return m_vwap_calc.Init(custom_start, custom_end, vol_type, true, max_history_days, tz_shift_hours); + } + +//+------------------------------------------------------------------+ +//| Main Calculation (Bounds-Safe O(1)) | +//+------------------------------------------------------------------+ +void CVScoreCalculator::Calculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], double &out_vscore[]) { - if(rates_total < m_period) + if(rates_total < m_period || CheckPointer(m_vwap_calc) == POINTER_INVALID) return; - if(CheckPointer(m_vwap_calc) == POINTER_INVALID) - return; - -// 1. Manage Internal Buffers and force strict chronological sorting +// 1. Safe Allocation of Internal Buffers if(ArraySize(m_vwap_buf) != rates_total) { - ArrayResize(m_vwap_buf, rates_total); - ArrayResize(m_vwap_odd, rates_total); + ArrayResize(m_vwap_buf, rates_total); + ArrayResize(m_vwap_odd, rates_total); ArrayResize(m_vwap_even, rates_total); + ArrayResize(m_price, rates_total); - ArraySetAsSeries(m_vwap_buf, false); - ArraySetAsSeries(m_vwap_odd, false); + ArraySetAsSeries(m_vwap_buf, false); + ArraySetAsSeries(m_vwap_odd, false); ArraySetAsSeries(m_vwap_even, false); + ArraySetAsSeries(m_price, false); } -// 2. Calculate VWAP Incrementally + if(ArraySize(out_vscore) != rates_total) + { + ArrayResize(out_vscore, rates_total); + ArraySetAsSeries(out_vscore, false); + ArrayInitialize(out_vscore, 0.0); + } + +// 2. Compute Underlying VWAP m_vwap_calc.Calculate(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, m_vwap_odd, m_vwap_even); -// 3. Calculate Standard Deviation of (Price - VWAP) - int start = (prev_calculated > m_period) ? prev_calculated - 1 : m_period; + int start = (prev_calculated > m_period) ? (prev_calculated - 1) : (m_period - 1); + if(start < m_period - 1) + start = m_period - 1; +// 3. Compute V-Score (Standard Deviation Distance from VWAP) for(int i = start; i < rates_total; i++) { - double current_vwap = (m_vwap_odd[i] != EMPTY_VALUE && m_vwap_odd[i] != 0) ? m_vwap_odd[i] : m_vwap_even[i]; + m_price[i] = close[i]; + + double current_vwap = (m_vwap_odd[i] != EMPTY_VALUE && m_vwap_odd[i] > 0.0) ? m_vwap_odd[i] : m_vwap_even[i]; m_vwap_buf[i] = current_vwap; - if(current_vwap == 0 || current_vwap == EMPTY_VALUE) + if(current_vwap == 0.0 || current_vwap == EMPTY_VALUE) { out_vscore[i] = 0.0; continue; } - double sum_sq_diff = 0; + double sum_sq_diff = 0.0; for(int k = 0; k < m_period; k++) { int idx = i - k; - double p = close[idx]; + double p = m_price[idx]; double v = m_vwap_buf[idx]; - if(v == 0 || v == EMPTY_VALUE) + if(v == 0.0 || v == EMPTY_VALUE) v = p; double diff = p - v; sum_sq_diff += diff * diff; } - double std_dev = MathSqrt(sum_sq_diff / m_period); + double std_dev = MathSqrt(sum_sq_diff / (double)m_period); if(std_dev > 1.0e-9) out_vscore[i] = (close[i] - current_vwap) / std_dev;