//+------------------------------------------------------------------+
//|                                                   Stochastic.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
#property description "Stochastic Oscillator"
#property strict

#property indicator_separate_window
#property indicator_minimum    0
#property indicator_maximum    100
#property indicator_buffers    6
#property indicator_color1     Blue
#property indicator_color2     Gray
#property indicator_color6     Red
#property indicator_level1     25.0
#property indicator_level2     75.0
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT
#define Name WindowExpertName()
//--- input parameters
input int InpKPeriod=21; // K Period
input int InpDPeriod=10; // D Period
input int InpSlowing=10; // Slowing
input int periperi = 200; // Show Arrows up to X candles away
input int wdth = 1; // Arrow width
input color uparrcol = clrBlue; // Up-swing arrow color
input color dnarrcol = clrRed; // Down-swing arrow color
input bool emailen = false; // Enable/Disable e-mail alert
//--- buffers
double ExtMainBuffer[];
double ExtSignalBuffer[];
double ExtHighesBuffer[];
double ExtLowesBuffer[];
double newval[];
double finx[];
double Pip;
//---
int draw_begin1=0;
int draw_begin2=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   string short_name;
   Pip = (_Point * MathPow(10, MathMod(_Digits, 2)));
//--- 2 additional buffers are used for counting.
   IndicatorBuffers(6);
   SetIndexBuffer(2, ExtHighesBuffer);
   SetIndexBuffer(3, ExtLowesBuffer);
//--- indicator lines
   SetIndexStyle(0,DRAW_LINE,0,2);
   SetIndexBuffer(0, ExtMainBuffer);
   SetIndexStyle(1,DRAW_LINE,1);
   SetIndexBuffer(1, ExtSignalBuffer);
//--- name for DataWindow and indicator subwindow label
   short_name="Sto("+IntegerToString(InpKPeriod)+","+IntegerToString(InpDPeriod)+","+IntegerToString(InpSlowing)+")";
   IndicatorShortName(short_name);
   SetIndexLabel(0,short_name);
   SetIndexLabel(1,"Signal");
   SetIndexStyle(4,DRAW_NONE);
   SetIndexBuffer(4, newval);
   SetIndexLabel(4,"GraalUn");
   SetIndexStyle(5,DRAW_LINE,0,2);
   SetIndexBuffer(5, finx);
   SetIndexLabel(5,"GraalUn2");
//---
   draw_begin1=InpKPeriod+InpSlowing;
   draw_begin2=draw_begin1+InpDPeriod;
   SetIndexDrawBegin(0,draw_begin1);
   SetIndexDrawBegin(1,draw_begin2);
//--- initialization done
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Stochastic oscillator                                            |
//+------------------------------------------------------------------+
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    i,k,pos;
//--- check for bars count
   if(rates_total<=InpKPeriod+InpDPeriod+InpSlowing)
      return(0);
//--- counting from 0 to rates_total
   ArraySetAsSeries(ExtMainBuffer,false);
   ArraySetAsSeries(ExtSignalBuffer,false);
   ArraySetAsSeries(ExtHighesBuffer,false);
   ArraySetAsSeries(ExtLowesBuffer,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(close,false);
//---

   for (int x = Bars - 1; x >= 0; x--) {
      newval[x] = iCustom(_Symbol, PERIOD_CURRENT, "GRAALUn", 18, 0, x);
   }
   
   double destun[]; ArrayResize(destun, ArraySize(newval));
   ArraySetAsSeries(destun, true);
   
   ArrayCopy(destun, newval, 0, 0, ArraySize(newval));
   double minval = destun[ArrayMinimum(destun, 0, 0)];
   
   double todo[]; ArrayResize(todo, ArraySize(destun));
   ArraySetAsSeries(todo, true);
   
   for (int x = ArraySize(destun) - 1; x >= 0; x--){
     todo[x] = destun[x] + MathAbs(minval);
   }
   
   double maxval = todo[ArrayMaximum(todo, 0, 0)];
   double multi = 100/maxval;
   
   for (int x = ArraySize(todo) - 1; x >= 0; x--) {
      finx[x] = todo[x] * multi;
   }
      
   pos=InpKPeriod-1;
   if(pos+1<prev_calculated)
      pos=prev_calculated-2;
   else
     {
      for(i=0; i<pos; i++)
        {
         ExtLowesBuffer[i]=0.0;
         ExtHighesBuffer[i]=0.0;
        }
     }
//--- calculate HighesBuffer[] and ExtHighesBuffer[]
   for(i=pos; i<rates_total && !IsStopped(); i++)
     {
      double dmin=1000000.0;
      double dmax=-1000000.0;
      for(k=i-InpKPeriod+1; k<=i; k++)
        {
         if(dmin>close[k])
            dmin=close[k];
         if(dmax<close[k])
            dmax=close[k];
        }
      ExtLowesBuffer[i]=dmin;
      ExtHighesBuffer[i]=dmax;
     }
//--- %K line
   pos=InpKPeriod-1+InpSlowing-1;
   if(pos+1<prev_calculated)
      pos=prev_calculated-2;
   else
     {
      for(i=0; i<pos; i++)
         ExtMainBuffer[i]=0.0;
     }
//--- main cycle
   for(i=pos; i<rates_total && !IsStopped(); i++)
     {
      double sumlow=0.0;
      double sumhigh=0.0;
      for(k=(i-InpSlowing+1); k<=i; k++)
        {
         sumlow +=(close[k]-ExtLowesBuffer[k]);
         sumhigh+=(ExtHighesBuffer[k]-ExtLowesBuffer[k]);
        }
      if(sumhigh==0.0)
         ExtMainBuffer[i]=100.0;
      else
         ExtMainBuffer[i]=sumlow/sumhigh*100.0;
     }
//--- signal
   pos=InpDPeriod-1;
   if(pos+1<prev_calculated)
      pos=prev_calculated-2;
   else
     {
      for(i=0; i<pos; i++)
         ExtSignalBuffer[i]=0.0;
     }
   for(i=pos; i<rates_total && !IsStopped(); i++)
     {
      double sum=0.0;
      for(k=0; k<InpDPeriod; k++)
         sum+=ExtMainBuffer[i-k];
      ExtSignalBuffer[i]=sum/InpDPeriod;
     }
   
   bool new_check = false;
   static datetime new_time = 0;
   if (new_time < iTime(_Symbol, PERIOD_CURRENT, 0)) {
      new_time = iTime(_Symbol, PERIOD_CURRENT, 0);
      new_check = true;
   }
   if (new_check) {
      double buffo[]; ArrayResize(buffo, ArraySize(ExtMainBuffer));
      ArrayCopy(buffo, ExtMainBuffer, 0, 0, 0);
      ArraySetAsSeries(buffo, true);

      string obname;
      if (buffo[1] < finx[1] && buffo[0] > finx[0]) { if (emailen) SendMail("Stoch Alert MT4", _Symbol + " Stoch signal LONG!"); Alert(_Symbol + " Stoch signal LONG!"); obname = Name + "newarrup0"; burnarr(obname, Low[0] - Pip, 0, 236, clrBlack); ObjectSetInteger(0, obname, OBJPROP_ANCHOR, ANCHOR_TOP); ObjectSetInteger(0, obname, OBJPROP_COLOR, uparrcol);  }
      if (buffo[1] > finx[1] && buffo[0] < finx[0]) { if (emailen) SendMail("Stoch Alert MT4", _Symbol + " Stoch signal SHORT!"); Alert(_Symbol + " Stoch signal SHORT!"); obname = Name + "newarrdn0"; burnarr(obname, High[0] + Pip, 0, 238, clrBlack); ObjectSetInteger(0, obname, OBJPROP_ANCHOR, ANCHOR_BOTTOM); ObjectSetInteger(0, obname, OBJPROP_COLOR, dnarrcol); }
      past_arr();
      new_check = false;
   }
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+

void past_arr() {
   int periods = periperi;
   if (Bars < periods) periods = Bars - 20;
   double buffo[]; ArrayResize(buffo, ArraySize(ExtMainBuffer));
   ArrayCopy(buffo, ExtMainBuffer, 0, 0, 0);
   ArraySetAsSeries(buffo, true);
   
   ObjectsDeleteAll(0, Name + "arr");
   string obname;
   for (int x = 1; x <= periods; x++) {
      if (buffo[x + 1] < finx[x + 1] && buffo[x] > finx[x]) { obname = Name + "arrup" + IntegerToString(x); burnarr(obname, Low[x] - Pip, x, 236, clrBlue); ObjectSetInteger(0, obname, OBJPROP_ANCHOR, ANCHOR_TOP); ObjectSetInteger(0, obname, OBJPROP_COLOR, uparrcol); }
      if (buffo[x + 1] > finx[x + 1] && buffo[x] < finx[x]) { obname = Name + "arrdn" + IntegerToString(x); burnarr(obname, High[x] + Pip, x, 238, clrRed); ObjectSetInteger(0, obname, OBJPROP_ANCHOR, ANCHOR_BOTTOM); ObjectSetInteger(0, obname, OBJPROP_COLOR, dnarrcol); }
   }
}

//+CREATE ARROWS-----------------------------------------------------+
void burnarr(string name, double p, int t, int arrow, color col)
{
	if (ObjectFind(0, name) < 0)
		if (!ObjectCreate(0, name, OBJ_ARROW, 0, 0, 0))
		{
			Print("error: can't create label_object! code #", GetLastError());
		}
	ObjectSet(name, OBJPROP_TIME1, Time[t]);
	ObjectSet(name, OBJPROP_PRICE1, p);
	ObjectSet(name, OBJPROP_ARROWCODE, arrow);
	ObjectSet(name, OBJPROP_COLOR, col);
	ObjectSet(name, OBJPROP_WIDTH, wdth);
	ObjectSet(name, OBJPROP_BACK, false);
}
//+------------------------------------------------------------------+

void OnDeinit(const int reason)
{
	//------------------------------------------------------------------
	ObjectsDeleteAll(0, Name);
	//------------------------------------------------------------------
}