//+------------------------------------------------------------------+
//| FXP_ATRTrailingStop.mq5                                          |
//| FXPIPS ONLINE - https://fxpips.online/mt5-indicators/atr-trailing-stop/
//|                                                                  |
//| 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-trailing-stop/"
#property version   "1.00"
#property description "ATR trailing stop line that only moves in the trend direction."
#property description "Green below price in uptrends, red above price in downtrends."
#property description "Arrows mark direction flips on closed bars."

#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   3

#property indicator_label1  "ATR trailing stop"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrLimeGreen,clrTomato
#property indicator_width1  2

#property indicator_label2  "Flip up"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrLimeGreen
#property indicator_width2  2

#property indicator_label3  "Flip down"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrTomato
#property indicator_width3  2

input group "Stop"
input int    InpAtrPeriod  = 14;   // ATR period
input double InpMultiplier = 3.0;  // ATR multiplier
input bool   InpShowArrows = true; // Show flip arrows
input group "Alerts"
input bool   InpAlerts     = false; // Popup alert on a direction flip
input bool   InpPush       = false; // Push notification on a direction flip

double gStop[], gStopClr[], gBuy[], gSell[], gAtr[], gDir[];
int      gAtrN = 14, gBegin = 28;
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 Stop " + _Symbol + " " + TfName() + ": " + text;
   if(InpAlerts) Alert(msg);
   if(InpPush)   SendNotification(msg);
}

int OnInit()
{
   gAtrN  = MathMax(1, InpAtrPeriod);
   gBegin = gAtrN * 2;
   SetIndexBuffer(0, gStop,    INDICATOR_DATA);
   SetIndexBuffer(1, gStopClr, INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, gBuy,     INDICATOR_DATA);
   SetIndexBuffer(3, gSell,    INDICATOR_DATA);
   SetIndexBuffer(4, gAtr,     INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, gDir,     INDICATOR_CALCULATIONS);
   ArraySetAsSeries(gStop, false); ArraySetAsSeries(gStopClr, false);
   ArraySetAsSeries(gBuy, false);  ArraySetAsSeries(gSell, false);
   ArraySetAsSeries(gAtr, false);  ArraySetAsSeries(gDir, false);

   PlotIndexSetInteger(1, PLOT_ARROW, 233);
   PlotIndexSetInteger(2, PLOT_ARROW, 234);
   PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, 15);
   PlotIndexSetInteger(2, PLOT_ARROW_SHIFT, -15);
   for(int p = 0; p < 3; p++)
   {
      PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
      PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, gBegin);
   }
   IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("FXP ATR Trailing Stop (%d, %.1f)", gAtrN, InpMultiplier));
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   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(gBuy, EMPTY_VALUE);
      ArrayInitialize(gSell, EMPTY_VALUE);
      gAtr[0]     = high[0] - low[0];
      gStop[0]    = close[0] - InpMultiplier * gAtr[0];
      gDir[0]     = 1.0;
      gStopClr[0] = 0.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

      double loss = InpMultiplier * gAtr[i];
      double prev = gStop[i - 1];
      double st;
      if(close[i] > prev && close[i - 1] > prev)      st = MathMax(prev, close[i] - loss);
      else if(close[i] < prev && close[i - 1] < prev) st = MathMin(prev, close[i] + loss);
      else if(close[i] > prev)                        st = close[i] - loss;
      else                                            st = close[i] + loss;

      gStop[i]    = st;
      gDir[i]     = (close[i] > st) ? 1.0 : -1.0;
      gStopClr[i] = (gDir[i] > 0) ? 0.0 : 1.0;

      gBuy[i]  = EMPTY_VALUE;
      gSell[i] = EMPTY_VALUE;
      if(i < rates_total - 1 && i > gBegin && gDir[i] != gDir[i - 1])
      {
         if(gDir[i] > 0) { if(InpShowArrows) gBuy[i]  = low[i];  }
         else            { if(InpShowArrows) gSell[i] = high[i]; }
      }
   }

   if(prev_calculated > 0)
   {
      int b = rates_total - 2;
      if(b > gBegin && gDir[b] != gDir[b - 1])
         Notify(gDir[b] > 0 ? "ATR stop flipped UP" : "ATR stop flipped DOWN", time[rates_total - 1]);
   }
   return(rates_total);
}
//+------------------------------------------------------------------+
