//------------------------------------------------------------------
#property copyright "mladen"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_color1  clrLimeGreen
#property indicator_color2  clrOrange
#property indicator_color3  clrLimeGreen
#property indicator_color4  clrOrange
#property indicator_color5  clrOrange
#property strict

//
//
//
//
//

enum enDisplay
{
   en_lin,  // Display line
   en_his,  // Display colored bars
   en_all,  // Display colored lines and bars
};
 
extern ENUM_TIMEFRAMES TimeFrame    = PERIOD_CURRENT; // Time frame
extern int       OmaLength          = 25;      // One more average period
extern double    OmaSpeed           = 3.0;     // One more average "speed"
extern bool      OmaAdaptive        = true;    // One more average should be adaptive?
extern double    Sensitivity        = 0.5;     // Sensivity Factor
extern double    StepSize           = 50;      // Step Size period
extern int       Shift              = 0;       // Shift
extern bool      HighLow            = false;   // High/Low Mode Switch (more sensitive)
extern double    Filter             = 0;       // Filter to use for filtering (<=0 - no filtering)
extern double    FilterPeriod       = 0;       // Filter period to use ( when <= 0, same as oma period) 
extern enDisplay DisplayType        = en_lin;  // Display type
extern int       LinesWidth         = 3;       // Lines width (when lines are included in display)
extern int       BarsWidth          = 1;       // Bars width (when bars are included in display)
extern bool      alertsOn           = false;   // Turn alerts on?
extern bool      alertsOnCurrent    = true;    // Alerts oon current bar?
extern bool      alertsMessage      = true;    // Show alerts message?
extern bool      alertsSound        = false;   // Play alerts sound?
extern bool      alertsEmail        = false;   // Send alerts email?
extern bool      alertsNotification = false;   // Send alerts notification?
extern bool      Interpolate        = true;    // Interpolate in mtf mode

double histou[];
double histod[];
double LineBuffer[];
double DnBuffera[];
double DnBufferb[];
double smin[];
double smax[];
double trend[],count[];

string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,0,OmaLength,OmaSpeed,OmaAdaptive,Sensitivity,StepSize,0,HighLow,Filter,FilterPeriod,DisplayType,LinesWidth,BarsWidth,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsNotification,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(9);
      indicatorFileName = WindowExpertName();
      TimeFrame         = MathMax(TimeFrame,_Period);
   
      int lstyle = DRAW_LINE;       if (DisplayType==en_his) lstyle = DRAW_NONE;
      int hstyle = DRAW_HISTOGRAM;  if (DisplayType==en_lin) hstyle = DRAW_NONE;
         SetIndexBuffer(0,histou);     SetIndexStyle(0,hstyle,EMPTY,BarsWidth);  SetIndexShift(0,TimeFrame/_Period*Shift);
         SetIndexBuffer(1,histod);     SetIndexStyle(1,hstyle,EMPTY,BarsWidth);  SetIndexShift(1,TimeFrame/_Period*Shift);
         SetIndexBuffer(2,LineBuffer); SetIndexStyle(2,lstyle,EMPTY,LinesWidth); SetIndexShift(2,TimeFrame/_Period*Shift); 
         SetIndexBuffer(3,DnBuffera);  SetIndexStyle(3,lstyle,EMPTY,LinesWidth); SetIndexShift(3,TimeFrame/_Period*Shift); 
         SetIndexBuffer(4,DnBufferb);  SetIndexStyle(4,lstyle,EMPTY,LinesWidth); SetIndexShift(4,TimeFrame/_Period*Shift); 
         SetIndexBuffer(5,smin);
         SetIndexBuffer(6,smax);
         SetIndexBuffer(7,trend);
         SetIndexBuffer(8,count);
         IndicatorShortName("StepMA("+(string)OmaLength+","+(string)Sensitivity+","+(string)StepSize+")");
      
            OmaLength = MathMax(OmaLength,   1);
            OmaSpeed  = MathMax(OmaSpeed ,-1.5);
   return(0);
}
int deinit() { return(0); }     

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = MathMin(Bars-counted_bars,Bars-1); count[0] = limit;
         if (TimeFrame != _Period)
         {
            limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(8,0)*TimeFrame/_Period));
            if (trend[limit]==-1) CleanPoint(limit,DnBuffera,DnBufferb);
            for (int i=limit;i>=0 && !_StopFlag; i--)
            {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                     LineBuffer[i] = _mtfCall(2,y);
   	               DnBuffera[i]  = EMPTY_VALUE;
   	               DnBufferb[i]  = EMPTY_VALUE;
   	               histou[i]     = EMPTY_VALUE;
                     histod[i]     = EMPTY_VALUE;
                     trend[i]      = _mtfCall(7,y);
                     
                     //
                     //
                     //
                     //
                     //
                     
                      if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
                      #define _interpolate(buff) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
                      int n,k; datetime time = iTime(NULL,TimeFrame,y);
                         for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;	
                         for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++) _interpolate(LineBuffer);
            }
            for(int i=limit; i>=0; i--) 
            {
   	         if (trend[i]==-1) PlotPoint(i,DnBuffera,DnBufferb,LineBuffer);
   	         if (trend[i]==-1) { histou[i] = Low[i]; histod[i] = High[i]; }
               if (trend[i]== 1) { histod[i] = Low[i]; histou[i] = High[i]; }
            }
            return(0);
         }

   //
   //
   //
   //
   //
        
      if (trend[limit]==-1) CleanPoint(limit,DnBuffera,DnBufferb);
      for(int i=limit; i>=0; i--)
      {
         double thigh;
         double tlow;
         int fperiod = OmaLength; if (FilterPeriod>0) fperiod=(int)FilterPeriod;
            if (HighLow)
	               { thigh=iAverage(iFilter(High[i] ,Filter,fperiod,i,0),OmaLength,OmaSpeed,OmaAdaptive,i,0); tlow =iAverage(iFilter(Low[i]  ,Filter,fperiod,i,0),OmaLength,OmaSpeed,OmaAdaptive,i,7);} 	
	         else  { thigh=iAverage(iFilter(Close[i],Filter,fperiod,i,0),OmaLength,OmaSpeed,OmaAdaptive,i,0); tlow =iAverage(iFilter(Close[i],Filter,fperiod,i,1),OmaLength,OmaSpeed,OmaAdaptive,i,7);}
   	   LineBuffer[i] = iStepMa(Sensitivity,iATR(NULL,0,(int)StepSize,i),1.0,thigh,tlow,Close[i],i);
   	   DnBuffera[i]  = EMPTY_VALUE;
   	   DnBufferb[i]  = EMPTY_VALUE;
   	   histou[i]     = EMPTY_VALUE;
         histod[i]     = EMPTY_VALUE;
   	   if (trend[i]==-1) PlotPoint(i,DnBuffera,DnBufferb,LineBuffer);
   	   if (trend[i]==-1) { histou[i] = Low[i]; histod[i] = High[i]; }
         if (trend[i]== 1) { histod[i] = Low[i]; histou[i] = High[i]; }
      }
   
   //
   //
   //
   //
   //
   
   if (alertsOn)
   {
      int whichBar = (alertsOnCurrent) ? 0 : 1;
      if (trend[whichBar] != trend[whichBar+1])
      if (trend[whichBar] == 1)
            doAlert("up");
      else  doAlert("down");       
   }   
	return(0);	
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

#define filterInstances 2
double workFil[][filterInstances*3];

#define _fchange 0
#define _fachang 1
#define _fprice  2

double iFilter(double tprice, double filter, int period, int i, int instanceNo=0)
{
   if (filter<=0) return(tprice);
   if (ArrayRange(workFil,0)!= Bars) ArrayResize(workFil,Bars); i = Bars-i-1; instanceNo*=3;
   
   //
   //
   //
   //
   //
   
   workFil[i][instanceNo+_fprice]  = tprice; if (i<1) return(tprice);
   workFil[i][instanceNo+_fchange] = MathAbs(workFil[i][instanceNo+_fprice]-workFil[i-1][instanceNo+_fprice]);
   workFil[i][instanceNo+_fachang] = workFil[i][instanceNo+_fchange];

   for (int k=1; k<period && (i-k)>=0; k++) workFil[i][instanceNo+_fachang] += workFil[i-k][instanceNo+_fchange];
                                            workFil[i][instanceNo+_fachang] /= period;
    
   double stddev = 0; for (int k=0;  k<period && (i-k)>=0; k++) stddev += MathPow(workFil[i-k][instanceNo+_fchange]-workFil[i-k][instanceNo+_fachang],2);
          stddev = MathSqrt(stddev/(double)period); 
   double filtev = filter * stddev;
   if( MathAbs(workFil[i][instanceNo+_fprice]-workFil[i-1][instanceNo+_fprice]) < filtev ) workFil[i][instanceNo+_fprice]=workFil[i-1][instanceNo+_fprice];
        return(workFil[i][instanceNo+_fprice]);
}

//
//
//
//
//

double stored[][14];
              
#define E1  0
#define E2  1
#define E3  2
#define E4  3
#define E5  4
#define E6  5
#define res 6

//
//
//
//
//

double iAverage(double price, double averagePeriod, double tconst, bool adaptive, int i, int ashift=0)
{
   if (ArrayRange(stored,0) != Bars) ArrayResize(stored,Bars);
   if (averagePeriod <=1) return(price);
   int r = Bars-i-1; 
   
   double e1= (r>0) ? stored[r-1][E1+ashift] : price;  double e2= (r>0) ? stored[r-1][E2+ashift] : price;
   double e3= (r>0) ? stored[r-1][E3+ashift] : price;  double e4= (r>0) ? stored[r-1][E4+ashift] : price;
   double e5= (r>0) ? stored[r-1][E5+ashift] : price;  double e6= (r>0) ? stored[r-1][E6+ashift] : price;

   //
   //
   //
   //
   //

      if (adaptive && (averagePeriod > 1))
      {
         double minPeriod = averagePeriod/2.0;
         double maxPeriod = minPeriod*5.0;
         int    endPeriod = (int)MathCeil(maxPeriod);
         double tsignal   = (r>=endPeriod) ? MathAbs((price-stored[r-endPeriod][res+ashift])): 0;
         double noise     = 0.00000000001;

            for(int k=1; k<endPeriod && r-k>=0; k++) noise=noise+MathAbs(price-stored[r-k][res+ashift]);

         averagePeriod = ((tsignal/noise)*(maxPeriod-minPeriod))+minPeriod;
      }
      
      //
      //
      //
      //
      //
      
      double alpha = (2.0+tconst)/(1.0+tconst+averagePeriod);

      e1 = e1 + alpha*(price-e1); e2 = e2 + alpha*(e1-e2); double v1 = 1.5 * e1 - 0.5 * e2;
      e3 = e3 + alpha*(v1   -e3); e4 = e4 + alpha*(e3-e4); double v2 = 1.5 * e3 - 0.5 * e4;
      e5 = e5 + alpha*(v2   -e5); e6 = e6 + alpha*(e5-e6); double v3 = 1.5 * e5 - 0.5 * e6;

   //
   //
   //
   //
   //

   stored[r][E1+ashift]  = e1;  stored[r][E2+ashift] = e2;
   stored[r][E3+ashift]  = e3;  stored[r][E4+ashift] = e4;
   stored[r][E5+ashift]  = e5;  stored[r][E6+ashift] = e6;
   stored[r][res+ashift] = price;
   return(v3);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

double workStep[][3];
#define _smin   0
#define _smax   1
#define _trend  2

double iStepMa(double sensitivity, double stepSize, double stepMulti, double phigh, double plow, double pprice, int r)
{
   if (ArrayRange(workStep,0)!=Bars) ArrayResize(workStep,Bars);
   if (sensitivity == 0) sensitivity = 0.0001; r = Bars-r-1;
   if (stepSize    == 0) stepSize    = 0.0001;
      double result=pprice; 
	   double size = sensitivity*stepSize;

      //
      //
      //
      //
      //
      
      if (r==0)
      {
         workStep[r][_smax]  = phigh+2.0*size*stepMulti;
         workStep[r][_smin]  = plow -2.0*size*stepMulti;
         workStep[r][_trend] = 0;
         return(pprice);
      }

      //
      //
      //
      //
      //
      
      workStep[r][_smax]  = phigh+2.0*size*stepMulti;
      workStep[r][_smin]  = plow -2.0*size*stepMulti;
      workStep[r][_trend] = workStep[r-1][_trend];
            if (pprice>workStep[r-1][_smax]) workStep[r][_trend] =  1;
            if (pprice<workStep[r-1][_smin]) workStep[r][_trend] = -1;
            if (workStep[r][_trend] ==  1) { if (workStep[r][_smin] < workStep[r-1][_smin]) workStep[r][_smin]=workStep[r-1][_smin]; result = workStep[r][_smin]+size*stepMulti; }
            if (workStep[r][_trend] == -1) { if (workStep[r][_smax] > workStep[r-1][_smax]) workStep[r][_smax]=workStep[r-1][_smax]; result = workStep[r][_smax]-size*stepMulti; }
      trend[Bars-r-1] = workStep[r][_trend]; 

   return(result); 
} 

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
      if (previousAlert != doWhat || previousTime != Time[0]) {
          previousAlert  = doWhat;
          previousTime   = Time[0];

          //
          //
          //
          //
          //

          message = timeFrameToString(_Period)+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" Step Oma "+doWhat;
             if (alertsMessage)      Alert(message);
             if (alertsNotification) SendNotification(message);
             if (alertsEmail)        SendMail(StringConcatenate(Symbol()," Step Oma "),message);
             if (alertsSound)        PlaySound("alert2.wav");
      }
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>Bars-2) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (i>Bars-3) return;
   if (first[i+1] == EMPTY_VALUE)
         if (first[i+2] == EMPTY_VALUE) 
               { first[i]  = from[i]; first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
         else  { second[i] = from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else        { first[i]  = from[i];                          second[i] = EMPTY_VALUE; }
}

//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}
