From 49d53edcab058d62b543a0c1f7c33b3e0124a3a0 Mon Sep 17 00:00:00 2001 From: Toh4iem9 Date: Thu, 16 Jul 2026 15:46:48 +0200 Subject: [PATCH] refactor: Added Live Spread Fallback and Scale Guard for custom timeframes (e.g. M3) --- .../MyIndicators/Quant/SpreadCost_Pro.mq5 | 89 +++++++++++++------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/Indicators/MyIndicators/Quant/SpreadCost_Pro.mq5 b/Indicators/MyIndicators/Quant/SpreadCost_Pro.mq5 index 48f9ede..4c164e5 100644 --- a/Indicators/MyIndicators/Quant/SpreadCost_Pro.mq5 +++ b/Indicators/MyIndicators/Quant/SpreadCost_Pro.mq5 @@ -3,7 +3,7 @@ //| Copyright 2026, xxxxxxxx| //+------------------------------------------------------------------+ #property copyright "Copyright 2026, xxxxxxxx" -#property version "1.01" // Fixed Point conversion logic +#property version "1.03" // Added Live Spread Fallback and Scale Guard for custom timeframes (e.g. M3) #property description "Relative Spread Cost Indicator." #property description "Shows Spread as a percentage of Volatility (ATR)." @@ -20,8 +20,8 @@ //--- Plot: Cost Histogram #property indicator_label1 "Spread Cost %" #property indicator_type1 DRAW_COLOR_HISTOGRAM -// Color Index: 0=Cheap(Green), 1=Normal(Gray), 2=Expensive(Red) -#property indicator_color1 clrLime, clrGray, clrRed +// Color Index: 0=Cheap(MediumSeaGreen), 1=Normal(Silver), 2=Expensive(Crimson) +#property indicator_color1 clrMediumSeaGreen, clrSilver, clrCrimson #property indicator_style1 STYLE_SOLID #property indicator_width1 2 @@ -40,35 +40,43 @@ double BufColors[]; CATRCalculator *g_atr; //+------------------------------------------------------------------+ -//| Init | +//| Custom Indicator Initialization | //+------------------------------------------------------------------+ int OnInit() { - SetIndexBuffer(0, BufCost, INDICATOR_DATA); +//--- Bind buffers to index mapping + SetIndexBuffer(0, BufCost, INDICATOR_DATA); SetIndexBuffer(1, BufColors, INDICATOR_COLOR_INDEX); +//--- Enforce strict chronological safety (false = old to new) + ArraySetAsSeries(BufCost, false); + ArraySetAsSeries(BufColors, false); + +//--- Scale Guard: Fix indicator minimum to prevent separate window collapse on 0.0 values +//IndicatorSetDouble(INDICATOR_MINIMUM, 0.0); + string name = StringFormat("SpreadCost(ATR%d)", InpATRPeriod); IndicatorSetString(INDICATOR_SHORTNAME, name); IndicatorSetInteger(INDICATOR_DIGITS, 1); // Display as 15.2 % g_atr = new CATRCalculator(); - if(!g_atr.Init(InpATRPeriod, ATR_POINTS)) + if(CheckPointer(g_atr) == POINTER_INVALID || !g_atr.Init(InpATRPeriod, ATR_POINTS)) return INIT_FAILED; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ -//| Deinit | +//| Custom Indicator Deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int r) { - if(CheckPointer(g_atr) == POINTER_DYNAMIC) + if(CheckPointer(g_atr) != POINTER_INVALID) delete g_atr; } //+------------------------------------------------------------------+ -//| Calculate | +//| Custom Indicator Calculation Loop | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, @@ -81,58 +89,85 @@ int OnCalculate(const int rates_total, const long &volume[], const int &spread[]) { - if(rates_total < InpATRPeriod) { Print("SpreadCost Error: Not enough bars. Total: ", rates_total, " Required: ", InpATRPeriod); return 0; } -// DEBUG: Check spread data quality (only once per bar to avoid spam) + if(CheckPointer(g_atr) == POINTER_INVALID) + return 0; + +//--- Force strict chronological alignment on all price and spread input arrays + ArraySetAsSeries(time, false); + ArraySetAsSeries(open, false); + ArraySetAsSeries(high, false); + ArraySetAsSeries(low, false); + ArraySetAsSeries(close, false); + ArraySetAsSeries(spread, false); + +//--- Query the live real-time spread as a fallback for custom timeframes (e.g. M3) + int current_spread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); + +//--- DEBUG: Check spread data quality (only once per bar to avoid log spam) static datetime last_print = 0; - if(time[rates_total-1] != last_print) + if(time[rates_total - 1] != last_print) { - double test_spread = (double)spread[rates_total-1]; - PrintFormat("DEBUG [%s %s]: Bars=%d, Spread[Last]=%.1f, Point=%.5f", - _Symbol, EnumToString(Period()), rates_total, test_spread, Point()); - last_print = time[rates_total-1]; + double test_spread = (double)spread[rates_total - 1]; + PrintFormat("DEBUG [%s %s]: Bars=%d, Spread[Last]=%.1f, LiveSpread=%d, Point=%.5f", + _Symbol, EnumToString(Period()), rates_total, test_spread, current_spread, Point()); + last_print = time[rates_total - 1]; } -// 1. Calculate ATR (Returns Price Value, e.g. 50.5) +//--- 1. Calculate ATR (Returns Price Value) double atr_buf[]; g_atr.Calculate(rates_total, prev_calculated, open, high, low, close, atr_buf); - int start = (prev_calculated > 0) ? prev_calculated - 1 : InpATRPeriod; +//--- CRITICAL SAFEGUARD: Prevent fatal out-of-range crashes during history synchronization + if(ArraySize(atr_buf) < rates_total) + { + return 0; // Exit safely, wait for the next tick + } -// Pre-fetch Point value (Processor efficiency) + int start = (prev_calculated > 0) ? prev_calculated - 1 : InpATRPeriod; double pt = Point(); +//--- 2. Calculate Spread relative cost for(int i = start; i < rates_total; i++) { double current_atr_price = atr_buf[i]; - // spread[] is in Points (Integer). Convert to Price. - // Example: Index Spread = 20 points. Point = 0.5. Spread Value = 10.0 - double current_spread_price = (double)spread[i] * pt; + // Extract spread from array + double sp = (double)spread[i]; - if(current_atr_price > 0.000001) + // FALLBACK LOGIC: If historical spread on the active forming bar is 0, use live spread + if(i == rates_total - 1 && sp == 0.0 && current_spread > 0) { - // Formula: (Spread Value / ATR Value) * 100 + sp = (double)current_spread; + } + + // Convert integer spread Points to real Price Difference + double current_spread_price = sp * pt; + + if(current_atr_price > 1.0e-9) + { + // Cost Ratio Formula: (Spread Price / ATR Price) * 100.0 double cost_pct = (current_spread_price / current_atr_price) * 100.0; BufCost[i] = cost_pct; if(cost_pct <= InpCheapLevel) - BufColors[i] = 0.0; + BufColors[i] = 0.0; // Cheap (MediumSeaGreen) else if(cost_pct >= InpExpensiveLevel) - BufColors[i] = 2.0; + BufColors[i] = 2.0; // Expensive (Crimson) else - BufColors[i] = 1.0; + BufColors[i] = 1.0; // Normal (Silver) } else { BufCost[i] = 0.0; + BufColors[i] = 1.0; // Fallback to normal } }