//+------------------------------------------------------------------+
//|                                   keltner-channels-alert.mq4       |
//|  Plots Keltner Channels (EMA center line, upper/lower bands at    |
//|  EMA +/- ATR x multiplier) and alerts when price closes outside   |
//|  either band.                                                     |
//|  Source: web-forex (educational, free to use and modify)          |
//+------------------------------------------------------------------+
#property copyright "web-forex"
#property strict
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrOrange
#property indicator_color3 clrOrange

extern int    EMAPeriod     = 20;
extern int    ATRPeriod     = 10;
extern double ATRMultiplier = 2.0;
extern int    AppliedPrice  = PRICE_CLOSE;
extern bool   EnableAlert   = true;
extern bool   EnablePush    = false;

double MiddleBuffer[];
double UpperBuffer[];
double LowerBuffer[];

int lastBreak = 0; // 0 = none, 1 = closed above upper, -1 = closed below lower

int OnInit()
{
   SetIndexBuffer(0, MiddleBuffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "Keltner Middle (EMA)");

   SetIndexBuffer(1, UpperBuffer);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexLabel(1, "Keltner Upper");

   SetIndexBuffer(2, LowerBuffer);
   SetIndexStyle(2, DRAW_LINE);
   SetIndexLabel(2, "Keltner Lower");

   IndicatorShortName("Keltner Channels Alert (" + IntegerToString(EMAPeriod) + ")");
   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[])
{
   int start = rates_total - prev_calculated;
   if (start > rates_total - EMAPeriod - 1)
      start = rates_total - EMAPeriod - 1;
   if (start < 0)
      return(rates_total);

   for (int i = start; i >= 0; i--)
   {
      double ema = iMA(NULL, 0, EMAPeriod, 0, MODE_EMA, AppliedPrice, i);
      double atr = iATR(NULL, 0, ATRPeriod, i);

      MiddleBuffer[i] = ema;
      UpperBuffer[i]  = ema + atr * ATRMultiplier;
      LowerBuffer[i]  = ema - atr * ATRMultiplier;
   }

   CheckBreakSignal();
   return(rates_total);
}

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

   double ema = iMA(NULL, 0, EMAPeriod, 0, MODE_EMA, AppliedPrice, 1);
   double atr = iATR(NULL, 0, ATRPeriod, 1);
   double upper = ema + atr * ATRMultiplier;
   double lower = ema - atr * ATRMultiplier;
   double priceClose = Close[1];

   int brk = 0;
   if (priceClose > upper)
      brk = 1;
   else if (priceClose < lower)
      brk = -1;

   if (brk != 0 && brk != lastBreak)
   {
      string msg = (brk == 1)
         ? Symbol() + " " + EnumToString((ENUM_TIMEFRAMES)Period()) + ": price closed above the upper Keltner Channel band"
         : Symbol() + " " + EnumToString((ENUM_TIMEFRAMES)Period()) + ": price closed below the lower Keltner Channel band";

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

   if (brk != 0)
      lastBreak = brk;
   else
      lastBreak = 0;
}
