//+------------------------------------------------------------------+
//| FXP_ATRVolatilityRegime.mq5                                      |
//| FXPIPS ONLINE - https://fxpips.online/mt5-indicators/atr-volatility-regime/
//|                                                                  |
//| License: MIT. Copyright (c) 2026 FXPIPS ONLINE.                  |
//| Permission is granted to use, copy, modify and distribute this   |
//| software provided this notice is kept. The software is provided  |
//| "as is", without warranty of any kind.                           |
//|                                                                  |
//| Educational tool - not financial advice. Trading involves        |
//| substantial risk of loss. Test on a demo account first.          |
//+------------------------------------------------------------------+
#property copyright "FXPIPS ONLINE"
#property link      "https://fxpips.online/mt5-indicators/atr-volatility-regime/"
#property version   "1.00"
#property description "Percentile rank of ATR versus its own recent history (0-100)."
#property description "Bars are coloured as low, normal or high volatility regime."

#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 3
#property indicator_plots   1

#property indicator_label1  "ATR percentile"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrSteelBlue,clrSilver,clrOrangeRed
#property indicator_width1  3

input group "Regime"
input int    InpAtrPeriod = 14;    // ATR period
input int    InpLookback  = 100;   // Percentile lookback (bars)
input double InpLowLevel  = 20.0;  // Low regime below this percentile
input double InpHighLevel = 80.0;  // High regime above this percentile
input group "Alerts"
input bool   InpAlerts    = false; // Popup alert on a regime change
input bool   InpPush      = false; // Push notification on a regime change

double gRank[], gClr[], gAtr[];
int      gAtrN = 14, gLook = 100, gBegin = 128;
datetime gLastAlertBar = 0;

string TfName() { return StringSubstr(EnumToString(Period()), 7); }

void Notify(const string text, const datetime barTime)
{
   if(!InpAlerts && !InpPush) return;
   if(barTime == gLastAlertBar) return;
   gLastAlertBar = barTime;
   string msg = "FXP ATR Regime " + _Symbol + " " + TfName() + ": " + text;
   if(InpAlerts) Alert(msg);
   if(InpPush)   SendNotification(msg);
}

string RegimeName(const double c)
{
   if(c == 0.0) return("LOW");
   if(c == 2.0) return("HIGH");
   return("NORMAL");
}

int OnInit()
{
   gAtrN  = MathMax(1, InpAtrPeriod);
   gLook  = MathMax(10, InpLookback);
   gBegin = gAtrN * 2 + gLook;
   SetIndexBuffer(0, gRank, INDICATOR_DATA);
   SetIndexBuffer(1, gClr,  INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, gAtr,  INDICATOR_CALCULATIONS);
   ArraySetAsSeries(gRank, false);
   ArraySetAsSeries(gClr, false);
   ArraySetAsSeries(gAtr, false);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, gBegin);

   IndicatorSetInteger(INDICATOR_LEVELS, 2);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, InpLowLevel);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, InpHighLevel);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE, STYLE_DOT);
   IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("FXP ATR Regime (%d, %d)", gAtrN, gLook));
   IndicatorSetInteger(INDICATOR_DIGITS, 1);
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   // No indicator handles or chart objects are created by this indicator.
}

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[])
{
   if(rates_total < gBegin + 2) return(0);
   ArraySetAsSeries(time, false);
   ArraySetAsSeries(high, false);
   ArraySetAsSeries(low, false);
   ArraySetAsSeries(close, false);

   int start;
   if(prev_calculated <= 0 || prev_calculated > rates_total)
   {
      ArrayInitialize(gRank, EMPTY_VALUE);
      ArrayInitialize(gClr, 1.0);
      gAtr[0] = high[0] - low[0];
      start = 1;
      gLastAlertBar = time[rates_total - 1];
   }
   else
      start = prev_calculated - 1;

   for(int i = start; i < rates_total && !IsStopped(); i++)
   {
      double tr = MathMax(high[i] - low[i], MathMax(MathAbs(high[i] - close[i - 1]), MathAbs(low[i] - close[i - 1])));
      gAtr[i] = gAtr[i - 1] + (tr - gAtr[i - 1]) / gAtrN;   // Wilder smoothing

      if(i < gBegin) continue;
      int below = 0;
      for(int k = 1; k <= gLook; k++)
         if(gAtr[i - k] < gAtr[i]) below++;
      double rank = 100.0 * below / gLook;
      gRank[i] = rank;
      gClr[i]  = (rank < InpLowLevel) ? 0.0 : ((rank > InpHighLevel) ? 2.0 : 1.0);
   }

   if(prev_calculated > 0)
   {
      int b = rates_total - 2;
      if(b > gBegin && gClr[b] != gClr[b - 1])
         Notify("volatility regime changed to " + RegimeName(gClr[b]) + StringFormat(" (ATR percentile %.0f)", gRank[b]),
                time[rates_total - 1]);
   }
   return(rates_total);
}
//+------------------------------------------------------------------+
