// More information about this indicator can be found at:
// https://fxcodebase.com/code/viewtopic.php?f=38&p=151826#p151826

//+------------------------------------------------------------------------------------------------+
//|                                                            Copyright © 2023, Gehtsoft USA LLC  |
//|                                                                         http://fxcodebase.com  |
//+------------------------------------------------------------------------------------------------+
//|                                                                   Developed by : Mario Jemic   |
//|                                                                       mario.jemic@gmail.com    |
//|                                                        https://AppliedMachineLearning.systems  |
//|                                                                       https://mario-jemic.com/ |
//+------------------------------------------------------------------------------------------------+

//+------------------------------------------------------------------------------------------------+
//|                                           Our work would not be possible without your support. |
//+------------------------------------------------------------------------------------------------+
//|                                                               Paypal: https://goo.gl/9Rj74e    |
//|                                                             Patreon :  https://goo.gl/GdXWeN   |
//+------------------------------------------------------------------------------------------------+


#property copyright "Copyright © 2023, Gehtsoft USA LLC"
#property link      "http://fxcodebase.com"
#property version "1.0"
//
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   3
//
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_type3   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_color2  clrLavender
#property indicator_color3  clrDeepPink
#property indicator_level1  0.0
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT
//
input int InpKPeriod = 1; // K Period
input int InpDPeriod = 3; // D Period
input int InpSlowing = 11; // Slowing
input int CCI    = 34;
input double c1     = 0.5;
input double c2     = 0.5;
input double c3     = 0.5;
input int    p      = 6;
input int MaPeriod  = 61;
input ENUM_MA_METHOD MaMethod = MODE_SMA;
input int    InpPeriod  = 21;
//
enum alert
  {
   Off = 0, // Off
   Current = 1, // At current bar
   Previous = 2 // At previous closed bar
  };
input alert  notificationsOn       = 2;                      // Notifications
input bool   desktop_notifications = true;                   // Desktop MT4 notifications
input bool   email_notifications   = false;                  // Email notifications
input bool   push_notifications    = false;                  // Push mobile notifications
input bool   sound_notifications   = false;                  // Sound notifications
input string sound_file = "Tick.wav";                        // Choose a sound file for notifications
//
double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
double ExtMapBuffer4[];
double ExtMapBuffer5[];
double CCI1, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, max, min;
int plotStart;
//-------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0, ExtMapBuffer1, INDICATOR_DATA);
   SetIndexBuffer(1, ExtMapBuffer2, INDICATOR_DATA);
   SetIndexBuffer(2, ExtMapBuffer3, INDICATOR_DATA);
   SetIndexBuffer(3, ExtMapBuffer4, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, ExtMapBuffer5, INDICATOR_CALCULATIONS);
//---
   ArraySetAsSeries(ExtMapBuffer1, true);
   ArraySetAsSeries(ExtMapBuffer2, true);
   ArraySetAsSeries(ExtMapBuffer3, true);
   ArraySetAsSeries(ExtMapBuffer4, true);
   ArraySetAsSeries(ExtMapBuffer5, true);
//---
   plotStart = MathMax(InpPeriod, MathMax(CCI, MathMax(MaPeriod, InpSlowing)));
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, plotStart);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, plotStart);
   PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, plotStart);
   PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_NONE);
   PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_NONE);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int c;
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[])
  {
   double dResult1;
   int i;
   double a[];
   if(rates_total < 10)
      return(0);
   int pos, limit;
   if(c > 2)
      limit = rates_total - prev_calculated;
   else
      limit = rates_total;
   c++;
   for(pos = 0; pos < limit && !IsStopped(); pos++)
     {
      ExtMapBuffer4[pos] = iCCIMQL4(Symbol(), Period(), CCI, PRICE_TYPICAL, pos);
     }
// ArrayResize(ExtMapBuffer4,ArraySize(ExtMapBuffer4)+20);
   for(pos = 0; pos < limit && !IsStopped(); pos++)
     {
      ArrayInitialize(a, EMPTY_VALUE);
      ArrayResize(a, p);
      for(i = 0; i < p; i++)
        {
         //int size_a = ArraySize(a);
         // ArrayResize(a, p-1);
         a[i] = ExtMapBuffer4(pos + i) * c1
                + ExtMapBuffer4(pos + (i - 1)) * c2 - ExtMapBuffer4(pos + i) * c3
                + ExtMapBuffer4(pos + (i - 2)) * c2 - ExtMapBuffer4(pos + (i - 1)) * c3
                + ExtMapBuffer4(pos + (i - 3)) * c2 - ExtMapBuffer4(pos + (i - 2)) * c3
                + ExtMapBuffer4(pos + (i - 4)) * c2 - ExtMapBuffer4(pos + (i - 3)) * c3
                + ExtMapBuffer4(pos + (i - 5)) * c2 - ExtMapBuffer4(pos + (i - 4)) * c3;
        }
      double a_max = a[ArrayMaximum(a)];
      double a_min = a[ArrayMinimum(a)];
      max = a_max;
      min = a_min;
      dResult1 = (max + min);
      ExtMapBuffer1[pos] = dResult1;
      //pos--;
     }
//pos = start;
//iMAOnArray(ExtMapBuffer1, rates_total, prev_calculated, MaPeriod, MaMethod, 0, ExtMapBuffer3);
//ExponentialMAOnBuffer(rates_total, prev_calculated, 20, 20, ExtMapBuffer1, ExtMapBuffer5);
   for(int j = 0; j < limit; j++)
     {
      ExtMapBuffer2[j] = iStochasticMQL4(Symbol(), Period(), InpKPeriod, InpDPeriod, InpSlowing, 0, 0, 0, j);
      ExtMapBuffer3[j] = iMAOnArray(ExtMapBuffer1, 0, MaPeriod, 0, MaMethod, j);
     }
   if(notificationsOn > 0)
     {
      checkAlert();
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

bool alerted;
void checkAlert()
  {
   bool nb = IsNewBar();
   if(nb)
      alerted = false;
   /*
   1 cci
   2 sto
   3 ma
   */
   if(notificationsOn == 1 && !alerted)
     {
      if(ExtMapBuffer2[0] >= ExtMapBuffer3[0] && ExtMapBuffer2[1] < ExtMapBuffer3[1])
        {
         Notify(1); // sto cross ma up
         alerted = true;
        }
      if(ExtMapBuffer2[0] >= ExtMapBuffer1[0] && ExtMapBuffer2[1] < ExtMapBuffer1[1])
        {
         Notify(2); // sto cross cci up
         alerted = true;
        }
      if(ExtMapBuffer1[0] >= ExtMapBuffer3[0] && ExtMapBuffer1[1] < ExtMapBuffer3[1])
        {
         Notify(3); // cci cross ma up
         alerted = true;
        }
      //
      if(ExtMapBuffer2[0] <= ExtMapBuffer3[0] && ExtMapBuffer2[1] > ExtMapBuffer3[1])
        {
         Notify(-1); // sto cross ma down
         alerted = true;
        }
      if(ExtMapBuffer2[0] <= ExtMapBuffer1[0] && ExtMapBuffer2[1] > ExtMapBuffer1[1])
        {
         Notify(-2); // sto cross cci down
         alerted = true;
        }
      if(ExtMapBuffer1[0] <= ExtMapBuffer3[0] && ExtMapBuffer1[1] > ExtMapBuffer3[1])
        {
         Notify(-3); // cci cross ma down
         alerted = true;
        }
     }
   if(notificationsOn == 2 && nb)
     {
      if(ExtMapBuffer2[1] >= ExtMapBuffer3[1] && ExtMapBuffer2[2] < ExtMapBuffer3[2])
        {
         Notify(11); // sto cross ma up
         alerted = true;
        }
      if(ExtMapBuffer2[1] >= ExtMapBuffer1[1] && ExtMapBuffer2[2] < ExtMapBuffer1[2])
        {
         Notify(22); // sto cross cci up
         alerted = true;
        }
      if(ExtMapBuffer1[1] >= ExtMapBuffer3[1] && ExtMapBuffer1[2] < ExtMapBuffer3[2])
        {
         Notify(33); // cci cross ma up
         alerted = true;
        }
      //
      if(ExtMapBuffer2[1] <= ExtMapBuffer3[1] && ExtMapBuffer2[2] > ExtMapBuffer3[2])
        {
         Notify(-11); // sto cross ma down
         alerted = true;
        }
      if(ExtMapBuffer2[1] <= ExtMapBuffer1[1] && ExtMapBuffer2[2] > ExtMapBuffer1[2])
        {
         Notify(-22); // sto cross cci down
         alerted = true;
        }
      if(ExtMapBuffer1[1] <= ExtMapBuffer3[1] && ExtMapBuffer1[2] > ExtMapBuffer3[2])
        {
         Notify(-33); // cci cross ma down
         alerted = true;
        }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsValue(double val)
  {
   return val > 0 && val != EMPTY_VALUE;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsNewBar()
  {
   static datetime lastbar;
   datetime curbar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
   if(lastbar != curbar)
     {
      lastbar = curbar;
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Notify(int type)
  {
   string text = "StableFx: ";
   switch(type)
     {
      case 1:
         text += " Stochastic cross UP MA before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case 2:
         text += " Stochastic cross UP CCI before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case 3:
         text += " CCI cross UP MA before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -1:
         text += " Stochastic cross DOWN MA before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -2:
         text += " Stochastic cross DOWN CCI before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -3:
         text += " CCI cross DOWN MA before bar closes - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      //
      case 11:
         text += " Stochastic cross UP MA after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case 22:
         text += " Stochastic cross UP CCI after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case 33:
         text += " CCI cross UP MA after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -11:
         text += " Stochastic cross DOWN MA after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -22:
         text += " Stochastic cross DOWN CCI after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
      case -33:
         text += " CCI cross DOWN MA after bar closed - " + _Symbol + " " + GetTimeFrame(_Period);
         break;
     }
   text += " ";
   if(desktop_notifications)
      Alert(text);
   if(push_notifications)
      SendNotification(text);
   if(email_notifications)
      SendMail("MetaTrader Notification", text);
   if(sound_notifications)
      PlaySound(sound_file);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string GetTimeFrame(int lPeriod)
  {
   switch(lPeriod)
     {
      case PERIOD_M1:
         return ("M1");
      case PERIOD_M5:
         return ("M5");
      case PERIOD_M15:
         return ("M15");
      case PERIOD_M30:
         return ("M30");
      case PERIOD_H1:
         return ("H1");
      case PERIOD_H4:
         return ("H4");
      case PERIOD_D1:
         return ("D1");
      case PERIOD_W1:
         return ("W1");
      case PERIOD_MN1:
         return ("MN1");
      default:
         return((string)PERIOD_CURRENT);
     }
   return IntegerToString(lPeriod);
  }
//+------------------------------------------------------------------+
double ExtMapBuffer4(int k)
  {
   int s = ArraySize(ExtMapBuffer4) - 1;
   if(k > s || k < 0)
      return 0;
   else
      return ExtMapBuffer4[k];
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double iCCIMQL4(string symbol, int tf, int period, int price, int shift)
  {
   ENUM_TIMEFRAMES timeframe = TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price = PriceMigrate(price);
   int handle = iCCI(symbol, timeframe, period, price);
   if(handle < 0)
     {
      Print("The iCCI object is not created: Error", GetLastError());
      return(-1);
     }
   else
      return(CopyBufferMQL4(handle, 0, shift));
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES TFMigrate(int tf)
  {
   switch(tf)
     {
      case 0:
         return(PERIOD_CURRENT);
      case 1:
         return(PERIOD_M1);
      case 5:
         return(PERIOD_M5);
      case 15:
         return(PERIOD_M15);
      case 30:
         return(PERIOD_M30);
      case 60:
         return(PERIOD_H1);
      case 240:
         return(PERIOD_H4);
      case 1440:
         return(PERIOD_D1);
      case 10080:
         return(PERIOD_W1);
      case 43200:
         return(PERIOD_MN1);
      case 2:
         return(PERIOD_M2);
      case 3:
         return(PERIOD_M3);
      case 4:
         return(PERIOD_M4);
      case 6:
         return(PERIOD_M6);
      case 10:
         return(PERIOD_M10);
      case 12:
         return(PERIOD_M12);
      case 16385:
         return(PERIOD_H1);
      case 16386:
         return(PERIOD_H2);
      case 16387:
         return(PERIOD_H3);
      case 16388:
         return(PERIOD_H4);
      case 16390:
         return(PERIOD_H6);
      case 16392:
         return(PERIOD_H8);
      case 16396:
         return(PERIOD_H12);
      case 16408:
         return(PERIOD_D1);
      case 32769:
         return(PERIOD_W1);
      case 49153:
         return(PERIOD_MN1);
      default:
         return(PERIOD_CURRENT);
     }
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_APPLIED_PRICE PriceMigrate(int price)
  {
   switch(price)
     {
      case 1:
         return(PRICE_CLOSE);
      case 2:
         return(PRICE_OPEN);
      case 3:
         return(PRICE_HIGH);
      case 4:
         return(PRICE_LOW);
      case 5:
         return(PRICE_MEDIAN);
      case 6:
         return(PRICE_TYPICAL);
      case 7:
         return(PRICE_WEIGHTED);
      default:
         return(PRICE_CLOSE);
     }
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CopyBufferMQL4(int handle, int index, int shift)
  {
   double buf[];
   switch(index)
     {
      case 0:
         if(CopyBuffer(handle, 0, shift, 1, buf) > 0)
            return(buf[0]);
         break;
      case 1:
         if(CopyBuffer(handle, 1, shift, 1, buf) > 0)
            return(buf[0]);
         break;
      case 2:
         if(CopyBuffer(handle, 2, shift, 1, buf) > 0)
            return(buf[0]);
         break;
      case 3:
         if(CopyBuffer(handle, 3, shift, 1, buf) > 0)
            return(buf[0]);
         break;
      case 4:
         if(CopyBuffer(handle, 4, shift, 1, buf) > 0)
            return(buf[0]);
         break;
      default:
         break;
     }
   return(EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
double iMAOnArray(double &array[], int total, int period, int ma_shift, int ma_method, int shift)
  {
   double buf[], arr[];
   if(total == 0)
      total = ArraySize(array);
   if(total > 0 && total <= period)
      return(0);
   if(shift > total - period - ma_shift)
      return(0);
   switch(ma_method)
     {
      case MODE_SMA :
        {
         total = ArrayCopy(arr, array, 0, shift + ma_shift, period);
         if(ArrayResize(buf, total) < 0)
            return(0);
         double sum = 0;
         int    i, pos = total - 1;
         for(i = 1; i < period; i++, pos--)
            sum += arr[pos];
         while(pos >= 0)
           {
            sum += arr[pos];
            buf[pos] = sum / period;
            sum -= arr[pos + period - 1];
            pos--;
           }
         return(buf[0]);
        }
      case MODE_EMA :
        {
         if(ArrayResize(buf, total) < 0)
            return(0);
         double pr = 2.0 / (period + 1);
         int    pos = total - 2;
         while(pos >= 0)
           {
            if(pos == total - 2)
               buf[pos + 1] = array[pos + 1];
            buf[pos] = array[pos] * pr + buf[pos + 1] * (1 - pr);
            pos--;
           }
         return(buf[shift + ma_shift]);
        }
      case MODE_SMMA :
        {
         if(ArrayResize(buf, total) < 0)
            return(0);
         double sum = 0;
         int    i, k, pos;
         pos = total - period;
         while(pos >= 0)
           {
            if(pos == total - period)
              {
               for(i = 0, k = pos; i < period; i++, k++)
                 {
                  sum += array[k];
                  buf[k] = 0;
                 }
              }
            else
               sum = buf[pos + 1] * (period - 1) + array[pos];
            buf[pos] = sum / period;
            pos--;
           }
         return(buf[shift + ma_shift]);
        }
      case MODE_LWMA :
        {
         if(ArrayResize(buf, total) < 0)
            return(0);
         double sum = 0.0, lsum = 0.0;
         double price;
         int    i, weight = 0, pos = total - 1;
         for(i = 1; i <= period; i++, pos--)
           {
            price = array[pos];
            sum += price * i;
            lsum += price;
            weight += i;
           }
         pos++;
         i = pos + period;
         while(pos >= 0)
           {
            buf[pos] = sum / weight;
            if(pos == 0)
               break;
            pos--;
            i--;
            price = array[pos];
            sum = sum - lsum + price * period;
            lsum -= array[i];
            lsum += price;
           }
         return(buf[shift + ma_shift]);
        }
      default:
         return(0);
     }
   return(0);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
double iStochasticMQL4(string symbol, int tf, int Kperiod, int Dperiod, int slowing, int method, int field, int mode, int shift)
  {
   ENUM_TIMEFRAMES timeframe = TFMigrate(tf);
   ENUM_MA_METHOD ma_method = MethodMigrate(method);
   ENUM_STO_PRICE price_field = StoFieldMigrate(field);
   int handle = iStochastic(symbol, timeframe, Kperiod, Dperiod,
                            slowing, ma_method, price_field);
   if(handle < 0)
     {
      Print("The iStochastic object is not created: Error", GetLastError());
      return(-1);
     }
   else
      return(CopyBufferMQL4(handle, mode, shift));
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_STO_PRICE StoFieldMigrate(int field)
  {
   switch(field)
     {
      case 0:
         return(STO_LOWHIGH);
      case 1:
         return(STO_CLOSECLOSE);
      default:
         return(STO_LOWHIGH);
     }
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ENUM_MA_METHOD MethodMigrate(int method)
  {
   switch(method)
     {
      case 0:
         return(MODE_SMA);
      case 1:
         return(MODE_EMA);
      case 2:
         return(MODE_SMMA);
      case 3:
         return(MODE_LWMA);
      default:
         return(MODE_SMA);
     }
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------------------------------------+
//|                                                                    We appreciate your support. |
//+------------------------------------------------------------------------------------------------+
//|                                                               Paypal: https://goo.gl/9Rj74e    |
//|                                                             Patreon :  https://goo.gl/GdXWeN   |
//+------------------------------------------------------------------------------------------------+
//|                                                                   Developed by : Mario Jemic   |
//|                                                                       mario.jemic@gmail.com    |
//|                                                        https://AppliedMachineLearning.systems  |
//|                                                                       https://mario-jemic.com/ |
//+------------------------------------------------------------------------------------------------+

//+------------------------------------------------------------------------------------------------+
//|BitCoin                    : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF                                 |
//|Ethereum                   : 0x8C110cD61538fb6d7A2B47858F0c0AaBd663068D                         |
//|SOL Address                : 4tJXw7JfwF3KUPSzrTm1CoVq6Xu4hYd1vLk3VF2mjMYh                       |
//|Cardano/ADA                : addr1v868jza77crzdc87khzpppecmhmrg224qyumud6utqf6f4s99fvqv         |
//|Dogecoin Address           : DBGXP1Nc18ZusSRNsj49oMEYFQgAvgBVA8                                 |
//|SHIB Address               : 0x1817D9ebb000025609Bf5D61E269C64DC84DA735                         |
//|Binance(ERC20 & BSC only)  : 0xe84751063de8ade7c5fbff5e73f6502f02af4e2c                         |
//|BitCoin Cash               : 1BEtS465S3Su438Kc58h2sqvVvHK9Mijtg                                 |
//|LiteCoin                   : LLU8PSY2vsq7B9kRELLZQcKf5nJQrdeqwD                                 |
//+------------------------------------------------------------------------------------------------+
