//+------------------------------------------------------------------+
//|                                            StepChart_v7 600+.mq4 |
//|                                Copyright © 2014, TrendLaboratory |
//|            http://finance.groups.yahoo.com/group/TrendLaboratory |
//|                                   E-mail: igorad2003@yahoo.co.uk |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2014, TrendLaboratory"
#property link      "http://finance.groups.yahoo.com/group/TrendLaboratory"

#property indicator_chart_window
#property indicator_buffers   4
#property indicator_color1    LimeGreen
#property indicator_color2    OrangeRed
#property indicator_width1    4
#property indicator_width2    4
#property indicator_color3    LimeGreen
#property indicator_color4    OrangeRed
#property indicator_width3    0
#property indicator_width4    0


//---- input parameters
extern int     TimeFrame      =     0;   //TimeFrame in min 
extern int     Price          =     0;   //Applied Price: 0-C,1-O,2-H,3-L,4-Median,5-Typical,6-Weighted
extern double  StepSize       =    10;   //Size in pips(if StepSize=0 and VoltyLength=0 then StepSize = HiLoRange*VoltyRatio/100)
extern int     HiLoMode       =     0;   //Hi/Lo Mode: 0-off,1-on 
extern int     VoltyLength    =     0;   //Volty Length(0-will be used StepSize)
extern double  VoltyRatio     =     1;   //Volty Ratio
extern double  Multiplier     =     2;   //Reversal Multiplier 
extern int     PreSmooth      =     1;   //Pre-smoothing Length 
extern int     PreSmoothMode  =     3;   //Pre-smoothing Method  
extern int     StartMode      =     0;   //0-start price as is,1-multiple of Step
extern int     WickMode       =     0;   //Wick Mode: 0-off, 1-on 

extern string  alerts         = "--- Alerts & Emails ---";
extern int     AlertMode      =     0;
extern int     SoundsNumber   =     5;    //Number of sounds after Signal
extern int     SoundsPause    =     5;    //Pause in sec between sounds 
extern string  UpSound        = "alert.wav";
extern string  DnSound        = "alert2.wav";
extern int     EmailMode      =     0;    //0-on,1-off   
extern int     EmailsNumber   =     1;    //0-on,1-off
 
//---- indicator buffers
double UpBuffer[];
double DnBuffer[];
double UpWick[];
double DnWick[];
double sPrice[];
double trend[];


double   _point, MaxValue = -10000, MinValue = 100000000;
string   short_name, TF, IndicatorName, prevmess, prevemail;
datetime prevtime, preTime, prevTime, ptime;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   if(TimeFrame <= Period()) TimeFrame = Period();
   TF = tf(TimeFrame);
   if(TF  == "Unknown timeframe") TimeFrame = Period();
   
   IndicatorDigits(Digits);
//---- indicator line
   IndicatorBuffers(6);
   SetIndexBuffer(0,UpBuffer);
   SetIndexBuffer(1,DnBuffer);
   SetIndexBuffer(2,UpWick);
   SetIndexBuffer(3,DnWick);
   SetIndexBuffer(4,sPrice);
   SetIndexBuffer(5,trend);
   
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexStyle(3,DRAW_HISTOGRAM);
   
//---- name for DataWindow and indicator subwindow label
   switch(TimeFrame)
   {
   case 1     : TF = "M1" ; break;
   case 5     : TF = "M5" ; break;
   case 15    : TF = "M15"; break;
   case 30    : TF = "M30"; break;
   case 60    : TF = "H1" ; break;
   case 240   : TF = "H4" ; break;
   case 1440  : TF = "D1" ; break;
   case 10080 : TF = "W1" ; break;
   case 43200 : TF = "MN1"; break;
   } 
      
   short_name="StepChart["+TF+"]("+Price+","+DoubleToStr(StepSize,2)+")";
   IndicatorShortName(short_name);
   SetIndexLabel(0,"StepClose"+"["+TF+"]");
   SetIndexLabel(1,"StepOpen" +"["+TF+"]");
   SetIndexLabel(2,"StepHigh" +"["+TF+"]");
   SetIndexLabel(3,"StepLow"  +"["+TF+"]"); 
//----
   int begin = MathMax(2,Bars - iBars(NULL,TimeFrame)*TimeFrame/Period() + VoltyLength+PreSmooth); 
   SetIndexDrawBegin(0,begin);
   SetIndexDrawBegin(1,begin);
   SetIndexDrawBegin(2,begin);
   SetIndexDrawBegin(3,begin);
//----
  
   IndicatorName = WindowExpertName();
   _point = MarketInfo(Symbol(),MODE_POINT) * MathPow(10,Digits%2);
   
   return(0);
}

//+------------------------------------------------------------------+
//| StepChart_v7                                                     |
//+------------------------------------------------------------------+
int start()
{
   int shift, limit, counted_bars = IndicatorCounted();
   
   if(counted_bars > 0) limit = Bars - counted_bars - 1;
   if(counted_bars < 0) return(0);
  	if(counted_bars < 1)
   { 
   limit = Bars - 1;    
      for(int i=0;i<Bars-1;i++)
      { 
      UpBuffer[i] = EMPTY_VALUE;
      DnBuffer[i] = EMPTY_VALUE;
      UpWick[i]   = EMPTY_VALUE;
      DnWick[i]   = EMPTY_VALUE;
      }
   }   
	
	if(TimeFrame != Period())
	{
   limit = MathMax(limit,TimeFrame/Period());   
      
      for(shift = 0;shift < limit;shift++) 
      {	
      int y = iBarShift(NULL,TimeFrame,Time[shift]);
      
      UpBuffer[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,StepSize,HiLoMode,VoltyLength,VoltyRatio,Multiplier,PreSmooth,PreSmoothMode,StartMode,WickMode,
                                "",AlertMode,SoundsNumber,SoundsPause,UpSound,DnSound,EmailMode,EmailsNumber,0,y);
      DnBuffer[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,StepSize,HiLoMode,VoltyLength,VoltyRatio,Multiplier,PreSmooth,PreSmoothMode,StartMode,WickMode,
                                "",AlertMode,SoundsNumber,SoundsPause,UpSound,DnSound,EmailMode,EmailsNumber,1,y);
      
         if(WickMode > 0)
         {
         UpWick[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,StepSize,HiLoMode,VoltyLength,VoltyRatio,Multiplier,PreSmooth,PreSmoothMode,StartMode,WickMode,
                                 "",AlertMode,SoundsNumber,SoundsPause,UpSound,DnSound,EmailMode,EmailsNumber,2,y); 
         DnWick[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,StepSize,HiLoMode,VoltyLength,VoltyRatio,Multiplier,PreSmooth,PreSmoothMode,StartMode,WickMode,
                                 "",AlertMode,SoundsNumber,SoundsPause,UpSound,DnSound,EmailMode,EmailsNumber,3,y);
         }
      }  
	return(0);
	}
	else _StepChart(limit);
   
   
   return(0);	
}

void _StepChart(int limit)
{
   double step;
   
   if(VoltyLength == 0 && StepSize == 0)
   { 
      for(int shift = limit;shift >= 0;shift--)
      {
      MaxValue = MathMax(MaxValue,High[shift]); 
      MinValue = MathMin(MinValue,Low [shift]);
      }
   
   StepSize = NormalizeDouble((MaxValue - MinValue) * VoltyRatio/100/_point,Digits);
   }
   
   for(shift = limit;shift >= 0;shift--) 
   {	
   sPrice[shift] = StepPrice(Price,StepSize,HiLoMode,VoltyLength,VoltyRatio,Multiplier,PreSmooth,PreSmoothMode,StartMode,step,shift); 
    
   trend[shift]  = trend[shift+1];
                  
   if(sPrice[shift] > sPrice[shift+1] && sPrice[shift+1] > 0) trend[shift] = 1;
   if(sPrice[shift] < sPrice[shift+1]) trend[shift] =-1;    
  
   UpBuffer[shift] = EMPTY_VALUE; 
   DnBuffer[shift] = EMPTY_VALUE;
               
      if(trend[shift] > 0)
      {
      UpBuffer[shift] = sPrice[shift];
      DnBuffer[shift] = sPrice[shift+1];
      if(sPrice[shift] == sPrice[shift+1]) DnBuffer[shift] = sPrice[shift] - 0.001*Point; 
      }
      else
      if(trend[shift] < 0)
      {
      UpBuffer[shift] = sPrice[shift];
      DnBuffer[shift] = sPrice[shift+1];
      if(sPrice[shift] == sPrice[shift+1]) DnBuffer[shift] = sPrice[shift+1] + 0.001*Point;  
      }
             
         
      if(WickMode > 0)
      {
      UpWick[shift] = EMPTY_VALUE; 
      DnWick[shift] = EMPTY_VALUE;   
         
         if(trend[shift] > 0) 
         {
         UpWick[shift] = MathMax(High[shift],UpBuffer[shift]); 
         DnWick[shift] = MathMin(Low[shift] ,UpBuffer[shift]);
         }
         else
         if(trend[shift] < 0) 
         {
         DnWick[shift] = MathMax(High[shift],UpBuffer[shift]);
         UpWick[shift] = MathMin(Low[shift] ,DnBuffer[shift]);
         } 
      }
   }      
//----------   
   if(AlertMode > 0)
   {
   bool uptrend = trend[1] > 0 && trend[2] <= 0;                  
   bool dntrend = trend[1] < 0 && trend[2] >= 0;
        
      if(uptrend || dntrend)
      {
         if(isNewBar(TimeFrame))
         {
         BoxAlert(uptrend," : BUY Signal at " +DoubleToStr(Close[1],Digits));   
         BoxAlert(dntrend," : SELL Signal at "+DoubleToStr(Close[1],Digits)); 
         }
      
      WarningSound(uptrend,SoundsNumber,SoundsPause,UpSound,Time[1]);
      WarningSound(dntrend,SoundsNumber,SoundsPause,DnSound,Time[1]);
         
         if(EmailMode > 0)
         {
         EmailAlert(uptrend,"BUY" ," : BUY Signal at " +DoubleToStr(Close[1],Digits),EmailsNumber); 
         EmailAlert(dntrend,"SELL"," : SELL Signal at "+DoubleToStr(Close[1],Digits),EmailsNumber); 
         }
      }
   }
}   


int    stepprevtime, dir[2];
double sprice[2];

double StepPrice(int pricemode,double size,int hilo,int voltylen,double voltyr,double mult,int presm,int premode,int start,double& step,int bar)
{
   if(stepprevtime != Time[bar])
   {
   sprice[1]    = sprice[0];   
   dir[1]       = dir[0]; 
   stepprevtime = Time[bar]; 
   }
   
   if(voltylen > 0 && bar < Bars - MathMax(1,presm + voltylen))
   {
   double sum  = 0;
      
      if(hilo == 0)
      {
      for(int i=0;i<voltylen;i++) 
         sum += MathAbs(iMA(NULL,0,presm,0,premode,pricemode,bar+i) - iMA(NULL,0,presm,0,premode,pricemode,bar+1+i));
      }
      else
      {
      for(i=0;i<voltylen;i++) 
         sum += iMA(NULL,0,presm,0,premode,2,bar+i) - iMA(NULL,0,presm,0,premode,3,bar+i);
      }        
      
   if(size > 0) step = MathMax(size,MathRound(voltyr*sum/voltylen/_point))*_point;
   else step = MathMax(0.1,MathRound(voltyr*sum/voltylen/_point))*_point;
   }
   else 
   if(voltylen == 0) step = size*_point;
   
   
   step = NormalizeDouble(step,Digits);
    
      if(hilo == 0)
      {
      double hiprice = NormalizeDouble(iMA(NULL,0,presm,0,premode,pricemode,bar),Digits);
      double loprice = hiprice;
      }
      else
      {
      hiprice = NormalizeDouble(iMA(NULL,0,presm,0,premode,2,bar),Digits);
      loprice = NormalizeDouble(iMA(NULL,0,presm,0,premode,3,bar),Digits);
      }
  
   if(bar < Bars - MathMax(1,presm+voltylen))
   {
   sprice[0] = sprice[1];   
   dir[0]    = dir[1]; 
   
   if(dir[0] != 0) double value = NormalizeDouble(mult*step,Digits);
   else value  = NormalizeDouble(step,Digits);
   
   double price = sprice[0];
   if(hiprice > sprice[0]) price = hiprice;
   else
   if(loprice < sprice[0]) price = loprice;
      
   double hidel  = NormalizeDouble(price - sprice[0],Digits);
   double lodel  = NormalizeDouble(sprice[0] - price,Digits);
         
      if(dir[0] <= 0 && hidel < value && hidel >= 0) sprice[0] = sprice[1];
      else
      if(dir[0] >= 0 && lodel < value && lodel >= 0) sprice[0] = sprice[1];          
      else
      { 
         while(NormalizeDouble(MathAbs(price - sprice[0]),Digits) >= step) 
         {
    	      if(price > sprice[0]) sprice[0] = NormalizeDouble(sprice[0] + step,Digits);
		      if(price < sprice[0]) sprice[0] = NormalizeDouble(sprice[0] - step,Digits);
		   }
		}    
    
   double del = NormalizeDouble(sprice[0] - sprice[1],Digits);
                 
   if(del  >= value && dir[0] <= 0) dir[0] = 1;
   if(-del >= value && dir[0] >= 0) dir[0] =-1;    
         
   
   }
   else
   if(bar == Bars - MathMax(1,presm+voltylen))
   { 
      if(start == 0) sprice[0] = NormalizeDouble(iMA(NULL,0,presm,0,premode,pricemode,bar),Digits);
      else
      {
      if(step != 0.0) sprice[0] = MathRound(price/step) * step; else sprice[0] = price;         
      }
   
   dir[0] = 0;
   }
   return(sprice[0]);
}  

bool isNewBar(int tf)
{
   static datetime pTime;
   bool res=false;
   
   if(tf >= 0)
   {
      if (iTime(NULL,tf,0)!= pTime)
      {
      res=true;
      pTime=iTime(NULL,tf,0);
      }   
   }
   else res = true;
   
   return(res);
}

bool BoxAlert(bool cond,string text)   
{      
   string mess = IndicatorName + "("+Symbol()+","+TF + ")" + text;
   
   if (cond && mess != prevmess)
	{
	Alert (mess);
	prevmess = mess; 
	return(true);
	} 
  
   return(false);  
}

bool Pause(int sec)
{
   if(TimeCurrent() >= preTime + sec) {preTime = TimeCurrent(); return(true);}
   
   return(false);
}

void WarningSound(bool cond,int num,int sec,string sound,datetime ctime)
{
   static int i;
   
   if(cond)
   {
   if(ctime != ptime) i = 0; 
   if(i < num && Pause(sec)) {PlaySound(sound); ptime = ctime; i++;}       	
   }
}

bool EmailAlert(bool cond,string text1,string text2,int num)   
{      
   string subj = "New " + text1 +" Signal from " + IndicatorName + "!!!";    
   string mess = IndicatorName + "("+Symbol()+","+TF + ")" + text2;
   
   if (cond && mess != prevemail)
	{
	if(subj != "" && mess != "") for(int i=0;i<num;i++) SendMail(subj, mess);  
	prevemail = mess; 
	return(true);
	} 
  
   return(false);  
}	         

string tf(int timeframe)
{
   switch(timeframe)
   {
   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("Unknown timeframe");
   }
}