//+------------------------------------------------------------------+
//|                                              trix-alert.mq4       |
//|  Plots TRIX (the percentage rate of change of a triple-smoothed   |
//|  EMA of price) with a signal line, and alerts on a TRIX/Signal    |
//|  crossover. No built-in TRIX function in MQL4 — calculated        |
//|  directly from Close.                                             |
//|  Source: web-forex (educational, free to use and modify)          |
//+------------------------------------------------------------------+
#property copyright "web-forex"
#property strict
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrOrange
#property indicator_level1 0

extern int    TrixPeriod   = 14;
extern int    SignalPeriod = 9;
extern bool   EnableAlert  = true;
extern bool   EnablePush   = false;

double TrixBuffer[];
double SignalBuffer[];
double Ema1Buffer[];
double Ema2Buffer[];
double Ema3Buffer[];

int lastCross = 0; // 0 = none, 1 = TRIX above Signal, -1 = TRIX below Signal

int OnInit()
{
   SetIndexBuffer(0, TrixBuffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "TRIX");

   SetIndexBuffer(1, SignalBuffer);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexLabel(1, "Signal");

   SetIndexBuffer(2, Ema1Buffer);
   SetIndexStyle(2, DRAW_NONE);
   SetIndexBuffer(3, Ema2Buffer);
   SetIndexStyle(3, DRAW_NONE);
   SetIndexBuffer(4, Ema3Buffer);
   SetIndexStyle(4, DRAW_NONE);

   IndicatorShortName("TRIX Alert (" + IntegerToString(TrixPeriod) + "," + IntegerToString(SignalPeriod) + ")");
   return(INIT_SUCCEEDED);
}

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 < 2)
      return(rates_total);

   int start = rates_total - prev_calculated;
   if (start >= rates_total)
      start = rates_total - 1;
   if (start < 0)
      start = 0;

   double alpha = 2.0 / (TrixPeriod + 1.0);

   for (int i = start; i >= 0; i--)
   {
      if (i == rates_total - 1)
      {
         Ema1Buffer[i] = Close[i];
         Ema2Buffer[i] = Close[i];
         Ema3Buffer[i] = Close[i];
         TrixBuffer[i] = 0;
      }
      else
      {
         Ema1Buffer[i] = alpha * Close[i] + (1 - alpha) * Ema1Buffer[i + 1];
         Ema2Buffer[i] = alpha * Ema1Buffer[i] + (1 - alpha) * Ema2Buffer[i + 1];
         Ema3Buffer[i] = alpha * Ema2Buffer[i] + (1 - alpha) * Ema3Buffer[i + 1];
         TrixBuffer[i] = (Ema3Buffer[i + 1] != 0)
            ? (Ema3Buffer[i] - Ema3Buffer[i + 1]) / Ema3Buffer[i + 1] * 100.0
            : 0;
      }
   }

   for (int i = start; i >= 0; i--)
   {
      if (i > rates_total - SignalPeriod)
      {
         SignalBuffer[i] = 0;
         continue;
      }
      double sum = 0;
      for (int j = 0; j < SignalPeriod; j++)
         sum += TrixBuffer[i + j];
      SignalBuffer[i] = sum / SignalPeriod;
   }

   CheckCrossSignal();
   return(rates_total);
}

void CheckCrossSignal()
{
   if (!EnableAlert && !EnablePush)
      return;

   double trixLast   = TrixBuffer[1];
   double signalLast = SignalBuffer[1];
   double trixPrev   = TrixBuffer[2];
   double signalPrev = SignalBuffer[2];

   bool bullishCross = (trixPrev <= signalPrev && trixLast > signalLast);
   bool bearishCross = (trixPrev >= signalPrev && trixLast < signalLast);

   int cross = 0;
   if (bullishCross)
      cross = 1;
   else if (bearishCross)
      cross = -1;

   if (cross != 0 && cross != lastCross)
   {
      string msg = (cross == 1)
         ? Symbol() + " " + EnumToString((ENUM_TIMEFRAMES)Period()) + ": TRIX crossed above its Signal line"
         : Symbol() + " " + EnumToString((ENUM_TIMEFRAMES)Period()) + ": TRIX crossed below its Signal line";

      if (EnableAlert)
         Alert(msg);
      if (EnablePush)
         SendNotification(msg);
   }

   if (cross != 0)
      lastCross = cross;
}
