//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_chart_window
#property indicator_buffers 6

#property indicator_color1  clrSpringGreen
#property indicator_color2  clrCrimson
#property indicator_color3  clrCrimson
#property indicator_color4  clrGray

#property indicator_width1  2
#property indicator_width2  2
#property indicator_width3  2

#property indicator_style4  STYLE_DOT

#property strict

//
//
//
enum  enMaTypes
      {
         ma_sma,                                        // Simple moving average
         ma_ema,                                        // Exponential moving average
         ma_smma,                                       // Smoothed moving average
         ma_lwma,                                       // Linear weighted moving average
         ma_linr                                        // Least squares moving average
      };
//
//

extern ENUM_TIMEFRAMES   TimeFrame   = PERIOD_CURRENT;  // Time frame
input int                MAPeriod    = 34;              // Ma period
input enMaTypes          MAType      = ma_linr;         // Ma type
input int                SPeriod     = 5;               // Signal period
input enMaTypes          SType       = ma_sma;          // Signal type
input ENUM_APPLIED_PRICE MAPrice     = PRICE_CLOSE;     // Ma price to use
extern string            note4        = ".........Alerts settings.........";
input bool             alertsOn        = false;           // Alerts on true/false?
input bool             alertsOnCurrent = false;          // Alerts on (still opened) bar true/false?
input bool             alertsMessage   = true;           // Alerts message true/false?
input bool             alertsSound     = true;          // Alerts sound true/false?
input bool             alertsNotify    = false;          // Alerts push notification true/false?
input bool             alertsEmail     = false;          // Alerts email true/false?
input string           soundFile       = "alert2.wav";   // Sound file
extern string            note5        = ".........Arrows settings.........";
extern bool              ShowArrows    = false;            // Arrows visible?
input bool             ArrowOnFirst    = false;             // Arrow on first bars true/false
input int              UpArrowSize     = 1;                // Up Arrow size
input int              DnArrowSize     = 1;                // Down Arrow size
input int              UpArrowCode     = 221;              // Up Arrow code
input int              DnArrowCode     = 222;              // Down arrow code
input double           UpArrowGap      = 0.5;              // Up Arrow gap        
input double           DnArrowGap      = 0.5;              // Dn Arrow gap
input color            UpArrowColor    = clrLimeGreen;     // Up Arrow Color
input color            DnArrowColor    = clrOrange;        // Down Arrow Color
input bool               Interpolate = true;            // Interpolate in multi time frame mode on/off?

double ma[],maUa[],maUb[],sgn[],upa[],dna[],valc[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,MAPeriod,MAType,SPeriod,SType,MAPrice,note4,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsNotify,alertsEmail,soundFile,note5,ShowArrows,ArrowOnFirst,UpArrowSize,DnArrowSize,UpArrowCode,DnArrowCode,UpArrowGap,DnArrowGap,UpArrowColor,DnArrowColor,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int OnInit()
{
   IndicatorBuffers(8);
   SetIndexBuffer(0,ma,   INDICATOR_DATA); SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(1,maUa, INDICATOR_DATA); SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(2,maUb, INDICATOR_DATA); SetIndexStyle(2,DRAW_LINE);
   
   SetIndexBuffer(3,sgn, INDICATOR_DATA); SetIndexStyle(3,DRAW_LINE); 
   
   SetIndexBuffer(4 ,upa);    SetIndexStyle(4,ShowArrows ? DRAW_ARROW :  DRAW_NONE,EMPTY,UpArrowSize,UpArrowColor); SetIndexArrow(4,UpArrowCode);
   SetIndexBuffer(5, dna);   SetIndexStyle(5,ShowArrows ? DRAW_ARROW :  DRAW_NONE,EMPTY,DnArrowSize,DnArrowColor); SetIndexArrow(5,DnArrowCode);
   
   SetIndexBuffer(6,valc);
   
   SetIndexBuffer(7,count);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period); 
   
   IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(TimeFrame)+" "+getAvgName(MAType)+" ("+(string)MAPeriod+")");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason){   }

//
//
//

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,limit=fmin(rates_total-prev_calculated+1,rates_total-1); count[0] = limit;
      if (TimeFrame!=_Period)
      {
         limit = (int)fmax(limit,fmin(rates_total-1,_mtfCall(7,0)*TimeFrame/_Period));
         if (valc[limit]== -1) CleanPoint(limit,maUa,maUb);        
         for (i=limit;i>=0 && !_StopFlag; i--)
         {
            int y = iBarShift(NULL,TimeFrame,time[i]);
             int x = y;
                if (ArrowOnFirst)
                      {  if (i<Bars-1) x = iBarShift(NULL,TimeFrame,Time[i+1]);               }
                else  {  if (i>0)      x = iBarShift(NULL,TimeFrame,Time[i-1]); else x = -1;  }
               ma[i]   = _mtfCall(0,y);
               sgn[i] = _mtfCall(3,y);
               
               maUa[i] = maUb[i]  = EMPTY_VALUE;
            
               valc[i] = _mtfCall(6,y);
                
                upa[i] = dna[i]  = EMPTY_VALUE;
                if (x!=y)
                     {
                       upa[i] = _mtfCall(4,y);
                       dna[i] = _mtfCall(5,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 dtime = iTime(NULL,TimeFrame,y);
                  for(n = 1; (i+n)<rates_total && time[i+n] >= dtime; n++) continue;	
                  for(k = 1; k<n && (i+n)<rates_total && (i+k)<rates_total; k++) 
                  {
                     _interpolate(ma); 
                     _interpolate(sgn);                    
                  }                    
         } 
         for(i=limit;i>=0; i--)
         {      
            if (valc[i] ==  -1)  PlotPoint(i,maUa,maUb,ma);           
         }
   return(rates_total);
   } 
      
   //
   //
   //
          
   if (valc[limit]== -1) CleanPoint(limit,maUa,maUb); 
   for(i=limit;i>=0; i--)
   {  
      ma[i]   = iCustomMa(MAType,iMA(NULL,0,1,0,MODE_SMA,MAPrice,i),MAPeriod,i,rates_total,0);  
      sgn[i]  = iCustomMa(SType,ma[i]                             ,SPeriod ,i,rates_total,1);    
      valc[i] = (i<rates_total-1) ? (ma[i]>sgn[i]) ? 1 : (ma[i]<sgn[i]) ? -1  : valc[i+1]  : 0; 
      maUa[i] = maUb[i] = EMPTY_VALUE; if (valc[i] == -1) PlotPoint(i,maUa,maUb,ma);

if (i<rates_total-1 && valc[i]!=valc[i+1])
        
      {
         if (valc [i] == 1)  upa[i] = fmin(ma[i],low[i] )-iATR(NULL,0,15,i)*UpArrowGap;
         if (valc [i] == -1) dna[i] = fmax(ma[i],high[i])+iATR(NULL,0,15,i)*DnArrowGap;
      }   
            
   }
   
if (alertsOn)
   {
      int whichBar = (alertsOnCurrent) ? 0 : 1;
      if (valc[whichBar] != valc[whichBar+1])
      
      {
            if (valc[whichBar] == 1)  doAlert("  signal up");
            if (valc[whichBar] == -1) doAlert("  signal down");       
      }  
   }       
return(rates_total);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//

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)+" ma + signal "+doWhat;
             if (alertsMessage) Alert(message);
             if (alertsNotify)  SendNotification(message);
             if (alertsEmail)   SendMail(_Symbol+" ma + signal  ",message);
             if (alertsSound)   PlaySound(soundFile);
      }
}

//
//
//

string getAvgName(int method)
{
   switch(method)
   {
      case ma_ema:  return("EMA");
      case ma_linr: return("LSMA");
      case ma_lwma: return("LWMA");
      case ma_sma:  return("SMA");
      case ma_smma: return("SMMA");
   }
return("");      
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

#define _maInstances 2
#define _maWorkBufferx1 1*_maInstances

double iCustomMa(int mode, double price, double length, int r, int bars, int instanceNo=0)
{
   r = bars-r-1;
   switch (mode)
   {
      case ma_sma   : return(iSma(price,(int)ceil(length),r,bars,instanceNo));
      case ma_ema   : return(iEma(price,length,r,bars,instanceNo));
      case ma_smma  : return(iSmma(price,(int)ceil(length),r,bars,instanceNo));
      case ma_lwma  : return(iLwma(price,(int)ceil(length),r,bars,instanceNo));
      case ma_linr  : return(iLinr(price,(int)ceil(length),r,bars,instanceNo));
      default       : return(price);
   }
}

//
//
//
//
//

double workSma[][_maWorkBufferx1];
double iSma(double price, int period, int r, int _bars, int instanceNo=0)
{
   if (ArrayRange(workSma,0)!= _bars) ArrayResize(workSma,_bars);

   workSma[r][instanceNo+0] = price;
   double avg = price; int k=1;  for(; k<period && (r-k)>=0; k++) avg += workSma[r-k][instanceNo+0];  
   return(avg/(double)k);
}

//
//
//
//
//

double workEma[][_maWorkBufferx1];
double iEma(double price, double period, int r, int _bars, int instanceNo=0)
{
   if (ArrayRange(workEma,0)!= _bars) ArrayResize(workEma,_bars);

   workEma[r][instanceNo] = price;
   if (r>0 && period>1)
          workEma[r][instanceNo] = workEma[r-1][instanceNo]+(2.0/(2.0+(period-1.0)/2.0))*(price-workEma[r-1][instanceNo]);
   return(workEma[r][instanceNo]);
}

//
//
//
//
//

double workSmma[][_maWorkBufferx1];
double iSmma(double price, double period, int r, int _bars, int instanceNo=0)
{
   if (ArrayRange(workSmma,0)!= _bars) ArrayResize(workSmma,_bars);

   workSmma[r][instanceNo] = price;
   if (r>1 && period>1)
          workSmma[r][instanceNo] = workSmma[r-1][instanceNo]+(price-workSmma[r-1][instanceNo])/period;
   return(workSmma[r][instanceNo]);
}

//
//
//
//
//

double workLwma[][_maWorkBufferx1];
double iLwma(double price, double period, int r, int _bars, int instanceNo=0)
{
   if (ArrayRange(workLwma,0)!= _bars) ArrayResize(workLwma,_bars);
   
   workLwma[r][instanceNo] = price; if (period<=1) return(price);
      double sumw = period;
      double sum  = period*price;

      for(int k=1; k<period && (r-k)>=0; k++)
      {
         double weight = period-k;
                sumw  += weight;
                sum   += weight*workLwma[r-k][instanceNo];  
      }             
      return(sum/sumw);
}

//
//
//
//
//

double workLinr[][_maWorkBufferx1];
double iLinr(double price, int period, int r, int bars, int instanceNo=0)
{
   if (ArrayRange(workLinr,0)!= bars) ArrayResize(workLinr,bars);

   //
   //
   //
   //
   //
   
      period = fmax(period,1);
      workLinr[r][instanceNo] = price;
      if (r<period) return(price);
         double lwmw = period; double lwma = lwmw*price;
         double sma  = price;
         for(int k=1; k<period && (r-k)>=0; k++)
         {
            double weight = period-k;
                   lwmw  += weight;
                   lwma  += weight*workLinr[r-k][instanceNo];  
                   sma   +=        workLinr[r-k][instanceNo];
         }             
   
   return(3.0*lwma/lwmw-2.0*sma/period);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>=Bars-3) 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-2) 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("");
}
 

