diff --git a/Indicators/MyIndicators/MovingAverage_MTF_Pro.mq5 b/Indicators/MyIndicators/MovingAverage_MTF_Pro.mq5 index 2ba67e6..65151f3 100644 --- a/Indicators/MyIndicators/MovingAverage_MTF_Pro.mq5 +++ b/Indicators/MyIndicators/MovingAverage_MTF_Pro.mq5 @@ -1,9 +1,9 @@ //+------------------------------------------------------------------+ //| MovingAverage_MTF_Pro.mq5 | -//| Copyright 2025, xxxxxxxx| +//| Copyright 2025, xxxxxxxx | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, xxxxxxxx" -#property version "1.10" // Optimized for incremental MTF calculation +#property version "2.00" // Unified MTF Engine Pattern #property description "Multi-Timeframe (MTF) Universal Moving Average." #property indicator_chart_window @@ -18,7 +18,10 @@ #include //--- Input Parameters --- -input ENUM_TIMEFRAMES InpUpperTimeframe = PERIOD_CURRENT; +input group "Timeframe Settings" +input ENUM_TIMEFRAMES InpUpperTimeframe = PERIOD_H1; // Target Timeframe + +input group "MA Settings" input int InpPeriod = 20; input ENUM_MA_TYPE InpMAType = SMA; input ENUM_APPLIED_PRICE_HA_ALL InpSourcePrice = PRICE_CLOSE_STD; @@ -26,80 +29,64 @@ input ENUM_APPLIED_PRICE_HA_ALL InpSourcePrice = PRICE_CLOSE_STD; //--- Indicator Buffers --- double BufferMA_MTF[]; -//--- Internal Buffer for HTF Calculation (Must be global to persist state) -double BufferMA_HTF_Internal[]; +//--- MTF Globals --- +double g_htf_buffer[]; // Internal buffer for HTF calculation +int g_htf_prev_calculated = 0; +double g_buf_open[], g_buf_high[], g_buf_low[], g_buf_close[]; // HTF Price Data //--- Global variables --- CMovingAverageCalculator *g_calculator; bool g_is_mtf_mode = false; ENUM_TIMEFRAMES g_calc_timeframe; -//+------------------------------------------------------------------+ -//| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { -//--- Resolve Timeframe +//--- 1. Resolve Timeframe g_calc_timeframe = InpUpperTimeframe; if(g_calc_timeframe == PERIOD_CURRENT) g_calc_timeframe = (ENUM_TIMEFRAMES)Period(); -//--- Validation if(g_calc_timeframe < Period()) { - Print("Error: The selected timeframe must be higher than or equal to the current chart timeframe."); + Print("Error: Target timeframe must be >= current timeframe."); return(INIT_FAILED); } g_is_mtf_mode = (g_calc_timeframe > Period()); -//--- Buffer Mapping +//--- 2. Buffer Setup SetIndexBuffer(0, BufferMA_MTF, INDICATOR_DATA); - ArraySetAsSeries(BufferMA_MTF, false); + ArraySetAsSeries(BufferMA_MTF, false); // Standard indexing PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); -//--- Initialize Calculator +//--- 3. Initialize Calculator if(InpSourcePrice <= PRICE_HA_CLOSE) g_calculator = new CMovingAverageCalculator_HA(); else g_calculator = new CMovingAverageCalculator(); - if(CheckPointer(g_calculator) == POINTER_INVALID || !g_calculator.Init(InpPeriod, InpMAType)) - { - Print("Failed to initialize Moving Average Calculator."); + if(!g_calculator.Init(InpPeriod, InpMAType)) return(INIT_FAILED); - } -//--- Set Short Name +//--- 4. Short Name string ma_name = EnumToString(InpMAType); - StringToUpper(ma_name); - string short_name; - if(g_is_mtf_mode) - short_name = StringFormat("%s MTF%s(%s,%d)", ma_name, (InpSourcePrice <= PRICE_HA_CLOSE ? " HA" : ""), EnumToString(g_calc_timeframe), InpPeriod); - else - short_name = StringFormat("%s%s(%d)", ma_name, (InpSourcePrice <= PRICE_HA_CLOSE ? " HA" : ""), InpPeriod); + string tf_str = g_is_mtf_mode ? (" " + EnumToString(g_calc_timeframe)) : ""; + string type = (InpSourcePrice <= PRICE_HA_CLOSE) ? " HA" : ""; + string short_name = StringFormat("%s%s%s(%d)", ma_name, type, tf_str, InpPeriod); IndicatorSetString(INDICATOR_SHORTNAME, short_name); - PlotIndexSetString(0, PLOT_LABEL, short_name); - PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod - 1); - IndicatorSetInteger(INDICATOR_DIGITS, _Digits); + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod); return(INIT_SUCCEEDED); } -//+------------------------------------------------------------------+ -//| Deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(CheckPointer(g_calculator) != POINTER_INVALID) delete g_calculator; - -// Free internal memory - ArrayFree(BufferMA_HTF_Internal); } -//+------------------------------------------------------------------+ -//| Calculation function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, @@ -112,98 +99,88 @@ int OnCalculate(const int rates_total, const long &volume[], const int &spread[]) { - if(rates_total < 2 || CheckPointer(g_calculator) == POINTER_INVALID) + if(rates_total < 2) return 0; - ENUM_APPLIED_PRICE price_type = (InpSourcePrice <= PRICE_HA_CLOSE) ? (ENUM_APPLIED_PRICE)(-(int)InpSourcePrice) : (ENUM_APPLIED_PRICE)InpSourcePrice; + ENUM_APPLIED_PRICE price_type = (InpSourcePrice <= PRICE_HA_CLOSE) ? + (ENUM_APPLIED_PRICE)(-(int)InpSourcePrice) : + (ENUM_APPLIED_PRICE)InpSourcePrice; //================================================================ -// MTF MODE +// MODE 1: Current Timeframe //================================================================ - if(g_is_mtf_mode) + if(!g_is_mtf_mode) { - //--- 1. Get HTF Bars Count - int htf_rates_total = (int)SeriesInfoInteger(_Symbol, g_calc_timeframe, SERIES_BARS_COUNT); - if(htf_rates_total < InpPeriod) - return 0; - - //--- 2. Manage HTF State (Incremental Logic) - static int htf_prev_calculated = 0; - - // Reset if chart was reset - if(prev_calculated == 0) - htf_prev_calculated = 0; - - //--- 3. Fetch HTF Data - // We copy the full history for data integrity, but the Calculator will optimize the math. - // Copying simple arrays is fast in MT5. - datetime htf_time[]; - double htf_open[], htf_high[], htf_low[], htf_close[]; - - // Only copy if we have new data or need full recalc - // For robustness, we copy full range, but we could optimize this further. - // Given the Engine optimization, copying is acceptable. - if(CopyTime(_Symbol, g_calc_timeframe, 0, htf_rates_total, htf_time) <= 0 || - CopyOpen(_Symbol, g_calc_timeframe, 0, htf_rates_total, htf_open) <= 0 || - CopyHigh(_Symbol, g_calc_timeframe, 0, htf_rates_total, htf_high) <= 0 || - CopyLow(_Symbol, g_calc_timeframe, 0, htf_rates_total, htf_low) <= 0 || - CopyClose(_Symbol, g_calc_timeframe, 0, htf_rates_total, htf_close) <= 0) - return 0; - - //--- 4. Resize Internal Buffer - if(ArraySize(BufferMA_HTF_Internal) != htf_rates_total) - ArrayResize(BufferMA_HTF_Internal, htf_rates_total); - - //--- 5. Calculate on HTF (Optimized) - // Pass htf_prev_calculated so the engine skips already calculated bars! - g_calculator.Calculate(htf_rates_total, htf_prev_calculated, price_type, htf_open, htf_high, htf_low, htf_close, BufferMA_HTF_Internal); - - // Update state - htf_prev_calculated = htf_rates_total; - - //--- 6. Map to Current Timeframe (Optimized Loop) - // We need to access time[] as series for iBarShift usually, but let's stick to linear mapping - // Standard iBarShift works with time. - - ArraySetAsSeries(htf_time, true); // HTF time as series for search? No, CopyTime is non-series by default. - ArraySetAsSeries(BufferMA_HTF_Internal, true); // Set as series to match iBarShift index logic (0 is newest) - ArraySetAsSeries(time, true); // Current time as series - ArraySetAsSeries(BufferMA_MTF, true); // Output as series - - // Determine where to start mapping - int limit = (prev_calculated > 0) ? rates_total - prev_calculated : rates_total; - // We iterate backwards from newest (0) to limit - - for(int i = 0; i < limit; i++) - { - // Find which HTF bar corresponds to the current bar time - int htf_shift = iBarShift(_Symbol, g_calc_timeframe, time[i], false); - - if(htf_shift >= 0 && htf_shift < htf_rates_total) - { - // BufferMA_HTF_Internal is set as series, so htf_shift (0=newest) works directly - BufferMA_MTF[i] = BufferMA_HTF_Internal[htf_shift]; - } - else - { - BufferMA_MTF[i] = EMPTY_VALUE; - } - } - - // Restore array indexing to default (false) - ArraySetAsSeries(BufferMA_HTF_Internal, false); - ArraySetAsSeries(time, false); - ArraySetAsSeries(BufferMA_MTF, false); - } -//================================================================ -// CURRENT TIMEFRAME MODE -//================================================================ - else - { - // Direct calculation with optimization g_calculator.Calculate(rates_total, prev_calculated, price_type, open, high, low, close, BufferMA_MTF); + return(rates_total); } +//================================================================ +// MODE 2: MTF Engine +//================================================================ + +//--- A. Get HTF Data Count + int htf_rates_total = iBars(_Symbol, g_calc_timeframe); + if(htf_rates_total < InpPeriod) + return 0; + +//--- B. Reset State on Full Recalc + if(prev_calculated == 0) + { + g_htf_prev_calculated = 0; + ArrayInitialize(BufferMA_MTF, EMPTY_VALUE); + } + +//--- C. Fetch HTF Data + if(CopyOpen(_Symbol, g_calc_timeframe, 0, htf_rates_total, g_buf_open) < 0 || + CopyHigh(_Symbol, g_calc_timeframe, 0, htf_rates_total, g_buf_high) < 0 || + CopyLow(_Symbol, g_calc_timeframe, 0, htf_rates_total, g_buf_low) < 0 || + CopyClose(_Symbol, g_calc_timeframe, 0, htf_rates_total, g_buf_close) < 0) + { + return 0; + } + +//--- D. Resize HTF Buffer + if(ArraySize(g_htf_buffer) != htf_rates_total) + ArrayResize(g_htf_buffer, htf_rates_total); + +//--- E. Calculate HTF (Incremental) + int htf_calc_start = (g_htf_prev_calculated > 0) ? g_htf_prev_calculated - 1 : 0; + + g_calculator.Calculate(htf_rates_total, htf_calc_start, price_type, + g_buf_open, g_buf_high, g_buf_low, g_buf_close, + g_htf_buffer); + + g_htf_prev_calculated = htf_rates_total; + +//--- F. Map to Current Chart (The Staircase) +// CRITICAL: Set HTF buffer as SERIES to match iBarShift (0 = Newest) + ArraySetAsSeries(g_htf_buffer, true); + +// Ensure 'time' is NOT series for our loop (0 = Oldest) + ArraySetAsSeries(time, false); + + int limit = (prev_calculated > 0) ? prev_calculated - 1 : 0; + + for(int i = limit; i < rates_total; i++) + { + datetime current_time = time[i]; + int htf_index = iBarShift(_Symbol, g_calc_timeframe, current_time, false); + + if(htf_index >= 0 && htf_index < htf_rates_total) + { + BufferMA_MTF[i] = g_htf_buffer[htf_index]; + } + else + { + BufferMA_MTF[i] = EMPTY_VALUE; + } + } + +// CRITICAL: Restore HTF buffer to non-series for next calculation + ArraySetAsSeries(g_htf_buffer, false); + return(rates_total); } //+------------------------------------------------------------------+ +//+------------------------------------------------------------------+