//+------------------------------------------------------------------+ //| L1VolatilityNormalizedSmoothed.mq5 | //| Copyright 2000-2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2026, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_label1 "L1VolatilityNormalizedSmoothed" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDeepSkyBlue #property indicator_width1 2 //--- input int BarsToShow = 1000; // Number of bars to calculate L1 input double CoefLambda = 0.015; // Lambda in lambda_max units input int SmoothPeriod = 10; // EMA smoothing period (1=no smoothing) //--- double NormVolSmooth[]; //+------------------------------------------------------------------+ //| Indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- prepare SetIndexBuffer(0, NormVolSmooth, INDICATOR_DATA); ArrayInitialize(NormVolSmooth, EMPTY_VALUE); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(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[], const int &spread[]) { //--- check bars static bool warned=false; if(rates_total < BarsToShow) { if(!warned) { Print("Waiting for enough bars: ",BarsToShow); warned=true; } ArrayInitialize(NormVolSmooth,EMPTY_VALUE); return 0; } //--- check new bar static datetime last_bar_time=0; bool new_bar=(time[0]!=last_bar_time); bool need_recalc= (prev_calculated==0) || new_bar || (rates_total!=prev_calculated); if(!need_recalc) return prev_calculated; last_bar_time=time[0]; int start = rates_total - BarsToShow; //--- for(int i = 0; i < start; i++) NormVolSmooth[i] = EMPTY_VALUE; //--- copy close prices vector price(BarsToShow); for(int i = 0; i < BarsToShow; i++) price[i] = close[start + i]; //--- vector l1(BarsToShow); price.L1TrendFilter(l1, CoefLambda, true); //--- compute normalized volatility vector VolNormalized(BarsToShow); double mean = 0, stddev = 0; for(int i = 0; i < BarsToShow; i++) mean += close[start + i] - l1[i]; mean /= BarsToShow; //--- for(int i = 0; i < BarsToShow; i++) stddev += MathPow(close[start + i] - l1[i] - mean, 2); stddev = MathSqrt(stddev / BarsToShow); //--- for(int i = 0; i < BarsToShow; i++) VolNormalized[i] = stddev > 0 ? (close[start + i] - l1[i]) / stddev : 0; //--- EMA smoothing vector Smooth(BarsToShow); double alpha = (SmoothPeriod <= 1) ? 1.0 : 2.0 / (SmoothPeriod + 1.0); //--- Smooth[0] = VolNormalized[0]; for(int i = 1; i < BarsToShow; i++) Smooth[i] = alpha * VolNormalized[i] + (1 - alpha) * Smooth[i - 1]; //--- copy to indicator buffer for(int i = 0; i < BarsToShow; i++) NormVolSmooth[start + i] = Smooth[i]; //--- return rates_total; } //+------------------------------------------------------------------+