//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_minimum 0
#property indicator_maximum 1
#property strict

//
//
//
//
//

enum enTimeFrames
{
   tf_cu  = PERIOD_CURRENT, // Current time frame
   tf_m1  = PERIOD_M1,      // 1 minute
   tf_m5  = PERIOD_M5,      // 5 minutes
   tf_m15 = PERIOD_M15,     // 15 minutes
   tf_m30 = PERIOD_M30,     // 30 minutes
   tf_h1  = PERIOD_H1,      // 1 hour
   tf_h4  = PERIOD_H4,      // 4 hours
   tf_d1  = PERIOD_D1,      // Daily
   tf_w1  = PERIOD_W1,      // Weekly
   tf_mn1 = PERIOD_MN1,     // Monthly
   tf_n1  = -1,             // First higher time frame
   tf_n2  = -2,             // Second higher time frame
   tf_n3  = -3              // Third higher time frame
};


extern enTimeFrames    TimeFrame          = tf_n1;      // Timeframe to use
extern int             PdfMaLength       = 10;                   // Pdfma Length
extern double          Sensitivity       = 5;                    // Sensitivity Factor
extern double          StepSize          = 20;                   // Constant Step Size
extern double          Variance          = 1;                    // Pdfma variance
extern double          Mean              = 0.0;                  // Pdfma mean
extern bool            HighLow           = false;                // High/Low Mode Switch (more sensitive)
extern int             HistoWidth        = 3;                    // Histogram bars width
extern color           UpHistoColor      = clrLimeGreen;         // Up histogram color
extern color           DnHistoColor      = clrRed;               // Down histogram color
extern bool            alertsOn          = true;                 // Turn alerts on?
extern bool            alertsOnCurrent   = false;                // Alerts on current (still opened) bar?
extern bool            alertsMessage     = true;                 // Alerts should display a message?
extern bool            alertsSound       = false;                // Alerts should play a sound?
extern bool            alertsNotify      = false;                // Alerts should send notification?
extern bool            alertsEmail       = false;                // Alerts should send an email?
extern string          soundFile         = "alert2.wav";         // Alerts sound file
extern bool            arrowsVisible     = true;                 // Show arrows?
extern bool            arrowsOnFirst     = false;                // Show arrows on first mtf bar or the next?          
extern string          arrowsIdentifier  = "spdf arrows1";       // Arrows id
extern double          arrowsUpperGap    = 0.5;                  // Arrows upper Gap
extern double          arrowsLowerGap    = 0.5;                  // Arrows lower gap
extern color           arrowsUpColor     = clrLimeGreen;         // Up arrow color
extern color           arrowsDnColor     = clrRed;               // Down arrows color
extern int             arrowsUpCode      = 241;                  // Up arrow code
extern int             arrowsDnCode      = 242;                  // Down arrow code
extern bool            arrowsUpSize      = 2;                    // Up arrow size
extern bool            arrowsDnSize      = 2;                    // Down arrow size
extern int             Shift             = 0;                    // Shift
//
//
//
//
//

double UpH[],DnH[],count[],LineBuffer[],smin[],smax[],trend[], _coeffs[];
 
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,tf_cu,PdfMaLength,Sensitivity,StepSize,Variance,Mean,HighLow,0,UpHistoColor,DnHistoColor,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsNotify,alertsEmail,soundFile,arrowsVisible,arrowsOnFirst,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,0,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#define maxx 3.5
int init()
{
   IndicatorBuffers(7);
   SetIndexBuffer(0,UpH); SetIndexStyle(0, DRAW_HISTOGRAM,EMPTY,HistoWidth,UpHistoColor); 
   SetIndexBuffer(1,DnH); SetIndexStyle(1, DRAW_HISTOGRAM,EMPTY,HistoWidth,DnHistoColor); 
   SetIndexBuffer(2,LineBuffer); 
   SetIndexBuffer(3,smin);
   SetIndexBuffer(4,smax);
   SetIndexBuffer(5,trend);
   SetIndexBuffer(6,count);
   
   IndicatorShortName(timeFrameToString(TimeFrame)+" StepMA("+(string)PdfMaLength+","+(string)Sensitivity+","+(string)StepSize+")");
      PdfMaLength = fmax(PdfMaLength,2);
      ArrayResize(_coeffs,PdfMaLength);   
         double step = maxx/(PdfMaLength-1); for(int i=0; i<PdfMaLength; i++) _coeffs[i] = pdf(i*step,Variance,Mean*maxx);

         indicatorFileName = WindowExpertName();
         TimeFrame         = (enTimeFrames)timeFrameValue(TimeFrame);  
         for (int i=0; i<7; i++) SetIndexShift(i,Shift*TimeFrame/Period());

   return(0);
}
int deinit()
{
   string lookFor       = arrowsIdentifier+":";
   int    lookForLength = StringLen(lookFor);
   for (int i=ObjectsTotal()-1; i>=0; i--)
   {
      string objectName = ObjectName(i);
         if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
   }
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int start()
{
   int i,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = fmin(Bars-counted_bars,Bars-1); count[0]=limit;
            if (TimeFrame!=_Period)
            {
               limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(6,0)*TimeFrame/_Period));
               for (i=limit;i>=0 && !_StopFlag; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                     UpH[i] = _mtfCall(0,y);
                     DnH[i] = _mtfCall(1,y);   
               }      
       return(0);
       } 

      //
      //
      //
      //
      //
        
      for(i=limit; i>=0; i--)
      {
         double thigh;
         double tlow;
            if (HighLow)
	               { thigh=iPdfma(Low[i ] ,PdfMaLength,_coeffs,i,0); tlow =iPdfma(High[i] ,PdfMaLength,_coeffs,i,1); } 	
	         else  { thigh=iPdfma(Close[i],PdfMaLength,_coeffs,i,0); tlow =iPdfma(Close[i],PdfMaLength,_coeffs,i,1); }

   	   LineBuffer[i] = iStepMa(Sensitivity,StepSize,Point,thigh,tlow,Close[i],i);
   	   if (trend[i] == 1) UpH[i] = 1;
   	   if (trend[i] ==-1) DnH[i] = 1;
   	   
   	   //
         //
         //
         //
         //
      
         if (arrowsVisible)
        {
          string lookFor = arrowsIdentifier+":"+(string)Time[i]; ObjectDelete(lookFor);            
          if (i<(Bars-1) && trend[i] != trend[i+1])
          {
             if (trend[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
             if (trend[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDnCode,arrowsDnSize, true);
          }
        }
     }
     manageAlerts();
	return(0);	
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

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 = 0; 
	   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] = 1;
         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); 
} 

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//    normal probability density function
//
//

#define Pi 3.141592653589793238462643

double pdf(double x, double variance=1.0, double mean=0) { return((1.0/MathSqrt(2*Pi*MathPow(variance,2))*MathExp(-MathPow(x-mean,2)/(2*MathPow(variance,2))))); }
double workPdfma[][2];
double iPdfma(double price, double period, double& coeffs[], int r, int instanceNo=0)
{
   if (ArraySize(workPdfma)!= Bars) ArrayResize(workPdfma,Bars); r = Bars-r-1;
   
   //
   //
   //
   //
   //
   
   workPdfma[r][instanceNo] = price;
      double sumw = coeffs[0];
      double sum  = coeffs[0]*price;

      for(int k=1; k<period && (r-k)>=0; k++)
      {
         double weight = coeffs[k];
                sumw  += weight;
                sum   += weight*workPdfma[r-k][instanceNo];  
      }             
      return(sum/sumw);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

   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("");
}
int timeFrameValue(int _tf)
{
   int add  = (_tf>=0) ? 0 : MathAbs(_tf);
   if (add != 0) _tf = _Period;
   int size = ArraySize(iTfTable); 
      int i =0; for (;i<size; i++) if (iTfTable[i]==_tf) break;
                                   if (i==size) return(_Period);
                                                return(iTfTable[(int)MathMin(i+add,size-1)]);
}
//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0;
      if (trend[whichBar] != trend[whichBar+1])
      {
         if (trend[whichBar]== 1) doAlert(whichBar,"up");
         if (trend[whichBar]==-1) doAlert(whichBar,"down");
      }    
   }
}

//
//
//
//
//

void doAlert(int forBar, string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[forBar]) {
       previousAlert  = doWhat;
       previousTime   = Time[forBar];

       //
       //
       //
       //
       //

       message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," ",timeFrameToString(_Period)+" StepMA pdf ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(Symbol()+" StepMA pdf ",message);
          if (alertsNotify)  SendNotification(message);
          if (alertsSound)   PlaySound(soundFile);
   }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void drawArrow(int i,color theColor,int theCode, int theSize, bool up)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //
      //
      //

      datetime time = Time[i]; if (arrowsOnFirst) time += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,time,0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_WIDTH,theSize);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}


  