//+------------------------------------------------------------------+
//|                                                UT_Bot_Alerts.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots   3

//--- Plot definitions
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrGreen, clrRed, clrNONE
#property indicator_width1  1
#property indicator_label1  "Open","High","Low","Close"

#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrGreen
#property indicator_width2  1
#property indicator_label2  "Buy Signal"

#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrRed
#property indicator_width3  1
#property indicator_label3  "Sell Signal"

//--- Enums
enum ENUM_ALERT_MODE
{
   ALERT_NONE,       // No Alerts
   ALERT_CURRENT,    // On Current Bar
   ALERT_CLOSED      // On Bar Close
};

//--- Inputs
input double          InpKeyValue   = 1.0;          // Key Value (Sensitivity)
input int             InpATRPeriod  = 10;           // ATR Period
input bool            InpHeikinAshi = false;        // Signals from Heikin Ashi Candles
input ENUM_ALERT_MODE InpAlertMode  = ALERT_CURRENT;// Alert Mode

//--- Buffers
double BufferOpen[];
double BufferHigh[];
double BufferLow[];
double BufferClose[];
double BufferColor[];
double BufferBuy[];
double BufferSell[];

//--- Internal Buffers (for calculation)
double BufferATR[];
double BufferTrailingStop[];
double BufferPos[]; 

//--- Global Variables
int      handleATR;
datetime LastAlertTime = 0;    // For Current Bar mode
datetime LastBarTime   = 0;    // For Closed Bar mode

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Indicator Buffers Mapping
   SetIndexBuffer(0, BufferOpen,  INDICATOR_DATA);
   SetIndexBuffer(1, BufferHigh,  INDICATOR_DATA);
   SetIndexBuffer(2, BufferLow,   INDICATOR_DATA);
   SetIndexBuffer(3, BufferClose, INDICATOR_DATA);
   SetIndexBuffer(4, BufferColor, INDICATOR_COLOR_INDEX);
   SetIndexBuffer(5, BufferBuy,   INDICATOR_DATA);
   SetIndexBuffer(6, BufferSell,  INDICATOR_DATA);
   
   SetIndexBuffer(7, BufferATR,          INDICATOR_CALCULATIONS);
   SetIndexBuffer(8, BufferTrailingStop, INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, BufferPos,          INDICATOR_CALCULATIONS);

   //--- Plot settings
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetInteger(1, PLOT_ARROW, 233); // Up Arrow
   PlotIndexSetInteger(2, PLOT_ARROW, 234); // Down Arrow
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0);

   //--- Get ATR Handle
   handleATR = iATR(NULL, 0, InpATRPeriod);
   if(handleATR == INVALID_HANDLE)
   {
      Print("Failed to create ATR handle");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(handleATR != INVALID_HANDLE)
      IndicatorRelease(handleATR);
}

//+------------------------------------------------------------------+
//| Custom 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[])
{
   if(handleATR == INVALID_HANDLE) return(0);

   //--- Update ATR Buffer
   int copied = CopyBuffer(handleATR, 0, 0, rates_total, BufferATR);
   if(copied <= 0) return(0);

   int limit = prev_calculated - 1;
   if(limit < 1) limit = 1;

   for(int i = limit; i < rates_total; i++)
   {
      //--- 1. Calculate Source (src)
      double src = close[i];
      if(InpHeikinAshi)
      {
         // Heikin Ashi Close = (O + H + L + C) / 4
         src = (open[i] + high[i] + low[i] + close[i]) / 4.0;
      }
      
      //--- 2. Calculate nLoss
      double xATR = BufferATR[i];
      double nLoss = InpKeyValue * xATR;

      //--- 3. Calculate xATRTrailingStop
      double prev_stop = BufferTrailingStop[i-1];
      double prev_src  = (i > 0) ? (InpHeikinAshi ? (open[i-1] + high[i-1] + low[i-1] + close[i-1]) / 4.0 : close[i-1]) : src;
      
      double current_stop = 0.0;
      
      if(src > prev_stop && prev_src > prev_stop)
      {
         current_stop = MathMax(prev_stop, src - nLoss);
      }
      else if(src < prev_stop && prev_src < prev_stop)
      {
         current_stop = MathMin(prev_stop, src + nLoss);
      }
      else if(src > prev_stop)
      {
         current_stop = src - nLoss;
      }
      else
      {
         current_stop = src + nLoss;
      }
      
      BufferTrailingStop[i] = current_stop;

      //--- 4. Calculate Position (pos)
      int pos = 0;
      int prev_pos = (int)BufferPos[i-1];
      
      if(prev_src < prev_stop && src > prev_stop)
      {
         pos = 1;
      }
      else if(prev_src > prev_stop && src < prev_stop)
      {
         pos = -1;
      }
      else
      {
         pos = prev_pos;
      }
      
      BufferPos[i] = pos;

      //--- 5. Signals
      bool isBuy = (pos == 1 && prev_pos != 1);
      bool isSell = (pos == -1 && prev_pos != -1);
      
      // Set Buffers for Arrows
      if(isBuy)
      {
         BufferBuy[i] = low[i] - 10 * _Point; 
      }
      else
      {
         BufferBuy[i] = 0.0;
      }
      
      if(isSell)
      {
         BufferSell[i] = high[i] + 10 * _Point;
      }
      else
      {
         BufferSell[i] = 0.0;
      }
      
      //--- 6. Candles and Colors
      BufferOpen[i] = open[i];
      BufferHigh[i] = high[i];
      BufferLow[i] = low[i];
      BufferClose[i] = close[i];
      
      if(src > current_stop)
      {
         BufferColor[i] = 0.0; // Green
      }
      else if(src < current_stop)
      {
         BufferColor[i] = 1.0; // Red
      }
      else
      {
         BufferColor[i] = 2.0; // None
      }
   }
   
   //--- Alert Logic
   if(InpAlertMode != ALERT_NONE && rates_total > 2)
   {
      datetime currentBarTime = time[rates_total - 1];
      
      // Alert On Bar Close
      if(InpAlertMode == ALERT_CLOSED)
      {
         if(LastBarTime != currentBarTime)
         {
            if(LastBarTime != 0) // Skip first run
            {
               // Check previous bar (rates_total - 2)
               if(BufferBuy[rates_total - 2] != 0.0) 
                  Alert("UT Long (Closed): ", Symbol());
                  
               if(BufferSell[rates_total - 2] != 0.0) 
                  Alert("UT Short (Closed): ", Symbol());
            }
            LastBarTime = currentBarTime;
         }
      }
      // Alert On Current Bar
      else if(InpAlertMode == ALERT_CURRENT)
      {
         // We only check the current bar (rates_total - 1)
         // Only alert if we haven't alerted for this bar's specific timestamp yet
         if(LastAlertTime != currentBarTime)
         {
            if(BufferBuy[rates_total - 1] != 0.0)
            {
               Alert("UT Long: ", Symbol());
               LastAlertTime = currentBarTime;
            }
            else if(BufferSell[rates_total - 1] != 0.0)
            {
               Alert("UT Short: ", Symbol());
               LastAlertTime = currentBarTime;
            }
         }
      }
   }
   
   return(rates_total);
}
