//+------------------------------------------------------------------+
//|                                PointAndFigureChart_v1.0 600+.mq4 |
//|                                Copyright © 2016, TrendLaboratory |
//|            http://finance.groups.yahoo.com/group/TrendLaboratory |
//|                                   E-mail: igorad2003@yahoo.co.uk |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2016, TrendLaboratory"
#property link      "http://finance.groups.yahoo.com/group/TrendLaboratory"
#property link      "http://newdigital-world.com/forum.php"


#property indicator_chart_window
#property indicator_buffers   4
#property indicator_color1    clrLimeGreen
#property indicator_color2    clrOrangeRed
#property indicator_width1    4
#property indicator_width2    4
#property indicator_color3    clrLimeGreen
#property indicator_color4    clrOrangeRed
#property indicator_width3    0
#property indicator_width4    0

enum ENUM_PRICE
{
   close,         // Close  
   open,          // Open  
   high,          // High  
   low,           // Low  
   median,        // Median  
   typical,       // Typical  
   weighted,      // Weighted Close
   haClose,       // Heiken Ashi Close
   haOpen,        // Heiken Ashi Open
   haHigh,        // Heiken Ashi High   
   haLow,         // Heiken Ashi Low
   haMedian,      // Heiken Ashi Median
   haTypical,     // Heiken Ashi Typical
   haWeighted,    // Heiken Ashi Weighted Close   
};

enum ENUM_MA_MODE
{
   SMA,                 // Simple Moving Average
   EMA,                 // Exponential Moving Average
   Wilder,              // Wilder Exponential Moving Average
   LWMA,                // Linear Weighted Moving Average
   SineWMA,             // Sine Weighted Moving Average
   TriMA,               // Triangular Moving Average
   LSMA,                // Least Square Moving Average (or EPMA, Linear Regression Line)
   SMMA,                // Smoothed Moving Average
   HMA,                 // Hull Moving Average by Alan Hull
   ZeroLagEMA,          // Zero-Lag Exponential Moving Average
   DEMA,                // Double Exponential Moving Average by Patrick Mulloy
   T3_basic,            // T3 by T.Tillson (original version)
   ITrend,              // Instantaneous Trendline by J.Ehlers
   Median,              // Moving Median
   GeoMean,             // Geometric Mean
   REMA,                // Regularized EMA by Chris Satchwell
   ILRS,                // Integral of Linear Regression Slope
   IE_2,                // Combination of LSMA and ILRS
   TriMAgen,            // Triangular Moving Average generalized by J.Ehlers
   VWMA,                // Volume Weighted Moving Average
   JSmooth,             // Smoothing by Mark Jurik
   SMA_eq,              // Simplified SMA
   ALMA,                // Arnaud Legoux Moving Average
   TEMA,                // Triple Exponential Moving Average by Patrick Mulloy
   T3,                  // T3 by T.Tillson (correct version)
   Laguerre,            // Laguerre filter by J.Ehlers
   BF2P,                // Two-pole modified Butterworth filter by J.Ehlers
   BF3P,                // Three-pole modified Butterworth filter by J.Ehlers
   SuperSmu,            // SuperSmoother by J.Ehlers
   Decycler,            // Simple Decycler by J.Ehlers
   eVWMA                // Modified eVWMA
};


#define pi 3.14159265358979323846  

//---- input parameters
input ENUM_TIMEFRAMES   TimeFrame            =        0;       // TimeFrame
input ENUM_PRICE        Price                =    close;       // Appplied to
input bool              UseHighLow           =    false;       // Use High/Low 
input double            BoxSize              =       10;       // Step Size in pips(if StepSize=0 and VoltyLength=0 then StepSize = HiLoRange*VoltyRatio/100)
input double            Multiplier           =        2;       // Reversal Multiplier 
input int               PreSmooth            =        1;       // Period of Pre-smoothing
input ENUM_MA_MODE      PreSmoothMode        =        0;       // Method of Pre-smoothing
input bool              ShowWicks            =    false;       // Show wicks

input string            pnfboxes             = "==== PnF Boxes: ====";
input bool              ShowPnFBoxes         =    false;       // Show Point and Figure
input color             UpTrendColor         = clrPaleGreen;   // UpTrend Color 
input color             DnTrendColor         = clrLightSalmon; // DownTrend Color  
input string            UniqueName           =    "PnF";

input string            alerts               = "==== Alerts & Emails: ====";
input bool              AlertOn              =    false;       //
input int               AlertShift           =        1;       // Alert Shift:0-current bar,1-previous bar
input int               SoundsNumber         =        5;       // Number of sounds after Signal
input int               SoundsPause          =        5;       // Pause in sec between sounds 
input string            UpTrendSound              = "alert.wav";
input string            DnTrendSound              = "alert2.wav";
input bool              EmailOn              =    false;       // 
input int               EmailsNumber         =        1;       //
input bool              PushNotificationOn   =    false;
 
//---- indicator buffers
double UpBuffer[];
double DnBuffer[];
double UpWick[];
double DnWick[];
double pnfPrice[];
double trend[];
double mPrice[];
double hiPrice[];
double loPrice[];
double oPrice[];
double aPrice1[];
double hiPrice1[];
double loPrice1[];
double oPrice1[];


int      timeframe, masize;
double   _point, MaxValue = -10000, MinValue = 100000000, xprice[2], pprice[2];
datetime pnftime, xtime[2], ptime[2];
string   short_name, TF, IndicatorName;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   timeframe = TimeFrame;
   if(timeframe <= Period()) timeframe = Period(); 
   TF = tf(timeframe);
   
   IndicatorDigits(Digits);
//---- indicator line
   IndicatorBuffers(17);
   SetIndexBuffer( 0,UpBuffer); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer( 1,DnBuffer); SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer( 2,  UpWick); SetIndexStyle(2,DRAW_HISTOGRAM);
   SetIndexBuffer( 3,  DnWick); SetIndexStyle(3,DRAW_HISTOGRAM);
   SetIndexBuffer( 4,pnfPrice);
   SetIndexBuffer( 5,   trend);
   SetIndexBuffer( 6,  mPrice);
   SetIndexBuffer( 7, hiPrice);
   SetIndexBuffer( 8, loPrice);
   SetIndexBuffer( 9,  oPrice);
   SetIndexBuffer(10, aPrice1);
   SetIndexBuffer(11,hiPrice1);
   SetIndexBuffer(12,loPrice1);
   SetIndexBuffer(13, oPrice1);
   
  
//---- 
   masize = averageSize(PreSmoothMode);     
      
   IndicatorName = WindowExpertName();
   short_name    = IndicatorName + "["+TF+"](" + Price + "," + DoubleToStr(BoxSize,0) + DoubleToStr(Multiplier,2)+")";
   
   IndicatorShortName(short_name);
   SetIndexLabel(0,"PnFClose"+"["+TF+"]");
   SetIndexLabel(1,"PnFOpen" +"["+TF+"]");
   SetIndexLabel(2,"PnFHigh" +"["+TF+"]");
   SetIndexLabel(3,"PnFLow"  +"["+TF+"]"); 
//----
   int begin = MathMax(2,Bars - iBars(NULL,timeframe)*timeframe/Period() + PreSmooth + 2); 
   SetIndexDrawBegin(0,begin);
   SetIndexDrawBegin(1,begin);
   SetIndexDrawBegin(2,begin);
   SetIndexDrawBegin(3,begin);
//----
   ArrayResize(tmp,masize);
   
   _point = Point*MathPow(10,Digits%2);
   
   return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
//----
   deleteObj(UniqueName);
 
//----
   return(0);
}
//+------------------------------------------------------------------+
//| PointAndFigureChart_v1.0 600+                                    |
//+------------------------------------------------------------------+
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,UseHighLow,BoxSize,Multiplier,PreSmooth,PreSmoothMode,ShowWicks,
                                "",ShowPnFBoxes,UpTrendColor,DnTrendColor,UniqueName, 
                                "",AlertOn,AlertShift,SoundsNumber,SoundsPause,UpTrendSound,DnTrendSound,EmailOn,EmailsNumber,PushNotificationOn,0,y);
                                
      DnBuffer[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,UseHighLow,BoxSize,Multiplier,PreSmooth,PreSmoothMode,ShowWicks,
                                "",ShowPnFBoxes,UpTrendColor,DnTrendColor,UniqueName,
                                "",AlertOn,AlertShift,SoundsNumber,SoundsPause,UpTrendSound,DnTrendSound,EmailOn,EmailsNumber,PushNotificationOn,1,y);
      
         if(ShowWicks)
         {
         UpWick[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,UseHighLow,BoxSize,Multiplier,PreSmooth,PreSmoothMode,ShowWicks,
                                 "",ShowPnFBoxes,UpTrendColor,DnTrendColor,UniqueName,
                                 "",AlertOn,AlertShift,SoundsNumber,SoundsPause,UpTrendSound,DnTrendSound,EmailOn,EmailsNumber,PushNotificationOn,2,y); 
         
         DnWick[shift] = iCustom(NULL,TimeFrame,IndicatorName,0,Price,UseHighLow,BoxSize,Multiplier,PreSmooth,PreSmoothMode,ShowWicks,
                                 "",ShowPnFBoxes,UpTrendColor,DnTrendColor,UniqueName,
                                 "",AlertOn,AlertShift,SoundsNumber,SoundsPause,UpTrendSound,DnTrendSound,EmailOn,EmailsNumber,PushNotificationOn,3,y);
         }
      }  
	return(0);
	}
	else _PnFChart(limit);
   
   
   return(0);	
}

void _PnFChart(int limit)
{
   double pnfopen;
  
   for(int shift = limit;shift >= 0;shift--) 
   {	
   trend[shift] = pnfChart(pnfPrice[shift],pnfopen,Price,BoxSize,Multiplier,UseHighLow,shift);
   
   if(pnfPrice[shift] < 0) continue; 
    
   trend[shift]  = trend[shift+1];
                  
   if(pnfPrice[shift] > pnfPrice[shift+1] && pnfPrice[shift+1] > 0) trend[shift] = 1;
   if(pnfPrice[shift] < pnfPrice[shift+1]) trend[shift] =-1;    
  
   UpBuffer[shift] = EMPTY_VALUE; 
   DnBuffer[shift] = EMPTY_VALUE;
             
      if(trend[shift] > 0)
      {
      UpBuffer[shift] = pnfPrice[shift];
      DnBuffer[shift] = pnfPrice[shift+1];
      if(pnfPrice[shift] == pnfPrice[shift+1]) DnBuffer[shift] = pnfPrice[shift] - 0.001*Point; 
      }
      else
      if(trend[shift] < 0)
      {
      UpBuffer[shift] = pnfPrice[shift];
      DnBuffer[shift] = pnfPrice[shift+1];
      if(pnfPrice[shift] == pnfPrice[shift+1]) DnBuffer[shift] = pnfPrice[shift+1] + 0.001*Point;  
      }
      
         
      if(ShowWicks)
      {
      UpWick[shift] = EMPTY_VALUE; 
      DnWick[shift] = EMPTY_VALUE;   
         
         if(trend[shift] > 0) 
         {
         UpWick[shift] = MathMax(hiPrice1[shift],UpBuffer[shift]); 
         DnWick[shift] = MathMin(loPrice1[shift],UpBuffer[shift]);
         }
         else
         if(trend[shift] < 0) 
         {
         DnWick[shift] = MathMax(hiPrice1[shift],UpBuffer[shift]);
         UpWick[shift] = MathMin(loPrice1[shift],DnBuffer[shift]);
         } 
      }
      
      if(ShowPnFBoxes)
      {
         if(pnftime != Time[shift])  
         {
         xtime[1]  = xtime[0];
         ptime[1]  = ptime[0];
         xprice[1] = xprice[0];
         pprice[1] = pprice[0];
         pnftime   = Time[shift];
         }
         
         xtime[0]  = xtime[1];
         ptime[0]  = ptime[1];
         xprice[0] = xprice[1];
         pprice[0] = pprice[1];
         
         string xname = UniqueName + " up ";   
         string pname = UniqueName + " dn ";
         
         ObjectDelete(0,xname + TimeToString(Time[shift]));
         
         if(trend[shift] > 0)
         {
            if(trend[shift] != trend[shift+1])
            {
            xtime[0]  = Time[shift];
            xprice[0] = pnfPrice[shift+1]; 
            
            if(xprice[0] > 0) SetBox(xname + TimeToString(xtime[0]),0,xtime[0],xprice[0] + BoxSize*_point,xtime[0],pnfPrice[shift],true,UpTrendColor);
            if(pprice[0] > 0) SetBox(pname + TimeToString(ptime[0]),0,ptime[0],pprice[0] - BoxSize*_point,xtime[0],xprice[0]      ,true,DnTrendColor);
            }
            else
            {
            if(xprice[0] > 0) SetBox(xname + TimeToString(xtime[0]),0,xtime[0],xprice[0] + BoxSize*_point,Time[shift],pnfPrice[shift],true,UpTrendColor);
            }
         }
         
         ObjectDelete(0,pname + TimeToString(Time[shift]));
         
         if(trend[shift] < 0)
         {
            if(trend[shift] != trend[shift+1])
            {
            ptime[0]  = Time[shift];
            pprice[0] = pnfPrice[shift+1]; 
            
            if(pprice[0] > 0) SetBox(pname + TimeToString(ptime[0]),0,ptime[0],pprice[0] - BoxSize*_point,ptime[0],pnfPrice[shift],true,DnTrendColor);
            if(xprice[0] > 0) SetBox(xname + TimeToString(xtime[0]),0,xtime[0],xprice[0] + BoxSize*_point,ptime[0],pprice[0]      ,true,UpTrendColor);
            }
            else
            {
            if(pprice[0] > 0) SetBox(pname + TimeToString(ptime[0]),0,ptime[0],pprice[0] - BoxSize*_point,Time[shift],pnfPrice[shift],true,DnTrendColor);
            }
         }
      }
   }      

//----------   
   
   if(AlertOn || EmailOn || PushNotificationOn)
   {
   bool uptrend = trend[AlertShift] > 0 && trend[AlertShift+1] <= 0;                  
   bool dntrend = trend[AlertShift] < 0 && trend[AlertShift+1] >= 0;
         
      if(uptrend || dntrend)
      {
         if(isNewBar(TimeFrame))
         {
            if(AlertOn)
            {
            BoxAlert(uptrend," : BUY Signal @ " +DoubleToStr(Close[AlertShift],Digits));   
            BoxAlert(dntrend," : SELL Signal @ "+DoubleToStr(Close[AlertShift],Digits)); 
            }
                   
            if(EmailOn)
            {
            EmailAlert(uptrend,"BUY" ," : BUY Signal @ " +DoubleToStr(Close[AlertShift],Digits),EmailsNumber); 
            EmailAlert(dntrend,"SELL"," : SELL Signal @ "+DoubleToStr(Close[AlertShift],Digits),EmailsNumber); 
            }
         
            if(PushNotificationOn)
            {
            PushAlert(uptrend," : BUY Signal @ " +DoubleToStr(Close[AlertShift],Digits));   
            PushAlert(dntrend," : SELL Signal @ "+DoubleToStr(Close[AlertShift],Digits)); 
            }
         }
         else
         {
            if(AlertOn)
            {
            WarningSound(uptrend,SoundsNumber,SoundsPause,UpTrendSound,Time[AlertShift]);
            WarningSound(dntrend,SoundsNumber,SoundsPause,DnTrendSound,Time[AlertShift]);
            }
         }   
      }
   }   
}   


int      dir[2];
double   upband[2], loband[2], pnfclose[2];
datetime pnfprevtime;

int pnfChart(double& close,double& open,int pricemode,double size,double mult,bool hilo,int bar)
{
   int app_price;
   double value, hiprice, loprice;
   
   if(pnfprevtime != Time[bar])
   {
   upband[1]   = upband[0]; 
   loband[1]   = loband[0];    
   dir[1]      = dir[0]; 
   pnfprevtime = Time[bar]; 
   }
   
   if(pricemode < 10) app_price = Price; else app_price = 0;
   
   mPrice[bar]  = getPrice(app_price,bar);
   hiPrice[bar] = High[bar];
   loPrice[bar] = Low [bar]; 
   oPrice[bar]  = Open[bar];
   
   if(bar > Bars - PreSmooth) return(-1);
   
   aPrice1 [bar] = allAveragesOnArray(0,mPrice ,PreSmooth,PreSmoothMode,masize,Bars - PreSmooth,bar);   
   hiPrice1[bar] = allAveragesOnArray(1,hiPrice,PreSmooth,PreSmoothMode,masize,Bars - PreSmooth,bar);  
   loPrice1[bar] = allAveragesOnArray(2,loPrice,PreSmooth,PreSmoothMode,masize,Bars - PreSmooth,bar);  
   oPrice1 [bar] = allAveragesOnArray(3,oPrice ,PreSmooth,PreSmoothMode,masize,Bars - PreSmooth,bar);    
   
   if(Price >= 7)
   {
   aPrice1[bar] = HeikenAshi(Price - 7,aPrice1[bar],hiPrice1[bar],loPrice1[bar],oPrice1[bar],Bars - PreSmooth - 1,bar,hiPrice1[bar],loPrice1[bar]);
   }
   
   
   
   double step = size*_point;
   
   if(!hilo)
   {
   hiprice = NormalizeDouble(MathFloor(aPrice1 [bar]/step)*step,Digits); 
   loprice = NormalizeDouble(MathCeil (aPrice1 [bar]/step)*step,Digits);
   }
   else
   {
   hiprice = NormalizeDouble(MathFloor(hiPrice1[bar]/step)*step,Digits);
   loprice = NormalizeDouble(MathCeil (loPrice1[bar]/step)*step,Digits);
   }
     
   if(bar < Bars - PreSmooth)
   {
   upband[0] = upband[1]; 
   loband[0] = loband[1];   
   dir[0]    = dir[1]; 
   
   if(dir[0] != 0) value = NormalizeDouble(mult*step,Digits); else value  = step;
      
      if(dir[0] > 0) 
      {
         if(hiprice > upband[0]) 
         {
         upband[0] = NormalizeDouble(hiprice,Digits); 
         loband[0] = NormalizeDouble(upband[0] - value,Digits);
         }
         else
         {
            if(loprice < NormalizeDouble(upband[0] - value,Digits)) 
            {
            dir[0] =-1; 
            loband[0] = NormalizeDouble(loprice,Digits);
            upband[0] = NormalizeDouble(loband[0] + value,Digits);
            }
         }
      }
      else
      if(dir[0] < 0)
      {
         if(loprice < loband[0])
         {   
         loband[0] = NormalizeDouble(loprice,Digits); 
         upband[0] = NormalizeDouble(loband[0] + value,Digits);
         }
         else
         {
            if(hiprice > NormalizeDouble(loband[0] + value,Digits)) 
            {
            dir[0] = 1; 
            upband[0] = NormalizeDouble(hiprice,Digits); 
            loband[0] = NormalizeDouble(upband[0] - value,Digits);
            }
         }
      }
   }    
   else
   if(bar == Bars - PreSmooth)
   { 
      if(Close[bar] >= Open[bar]) 
      {
      dir[0] = 1; 
      upband[0] = hiprice;
      loband[0] = loprice;// - value;
      }
      else
      {
      dir[0] =-1;   
      loband[0] = loprice;
      upband[0] = hiprice;//loprice + value; 
      }  
   }
   
   if(dir[0] > 0) close = upband[0]; else close = loband[0]; 
   
   
   return(dir[0]);
}


int averageSize(int mode)
{   
   int arraysize;
   
   switch(mode)
   {
   case 10: arraysize = 2; break;
   case 11: arraysize = 6; break;
   case 20: arraysize = 5; break;
   case 23: arraysize = 4; break;
   case 24: arraysize = 6; break;
   case 25: arraysize = 4; break;
   default: arraysize = 0; break;
   }
   
   return(arraysize);
}


double   tmp[][7][2], ma[7][4];
datetime prevtime[7];  

double allAveragesOnArray(int index,double& price[],int period,int mode,int arraysize,int cbars,int bar)
{
   if(period < 2) return(price[bar]);
   
   double MA[4];  
           
   if(mode == 1 || mode == 2 || mode == 7 || mode == 9 || mode == 10 || mode == 11 || mode == 12 || mode == 15 || mode == 20 || mode == 21 || (mode > 22 && mode < 30))  
   {
      if(prevtime[index] != Time[bar])
      {
      ma[index][3] = ma[index][2]; 
      ma[index][2] = ma[index][1]; 
      ma[index][1] = ma[index][0]; 
   
      if(arraysize > 0) for(int i=0;i<arraysize;i++) tmp[i][index][1] = tmp[i][index][0];
    
      prevtime[index] = Time[bar]; 
      }
   
      if(mode == 12 || mode == 15 || mode == 21 || (mode > 26 && mode < 30)) for(i=0;i<4;i++) MA[i] = ma[index][i]; 
   }
   
   switch(mode)
   {
   case 1 : ma[index][0] = EMAOnArray(price[bar],ma[index][1],period,cbars,bar); break;
   case 2 : ma[index][0] = WilderOnArray(price[bar],ma[index][1],period,cbars,bar); break;  
   case 3 : ma[index][0] = LWMAOnArray(price,period,bar); break;
   case 4 : ma[index][0] = SineWMAOnArray(price,period,bar); break;
   case 5 : ma[index][0] = TriMAOnArray(price,period,bar); break;
   case 6 : ma[index][0] = LSMAOnArray(price,period,bar); break;
   case 7 : ma[index][0] = SMMAOnArray(price,ma[index][1],period,cbars,bar); break;
   case 8 : ma[index][0] = HMAOnArray(price,period,cbars,bar); break;
   case 9 : ma[index][0] = ZeroLagEMAOnArray(price,ma[index][1],period,cbars,bar); break;
   case 10: ma[index][0] = DEMAOnArray(index,0,price[bar],period,1,cbars,bar); break;
   case 11: ma[index][0] = T3_basicOnArray(index,0,price[bar],period,0.7,cbars,bar); break;
   case 12: ma[index][0] = ITrendOnArray(price,MA,period,cbars,bar); break;
   case 13: ma[index][0] = MedianOnArray(price,period,bar); break;
   case 14: ma[index][0] = GeoMeanOnArray(price,period,cbars,bar); break;
   case 15: ma[index][0] = REMAOnArray(price[bar],MA,period,0.5,cbars,bar); break;
   case 16: ma[index][0] = ILRSOnArray(price,period,bar); break;
   case 17: ma[index][0] = IE2OnArray(price,period,bar); break;
   case 18: ma[index][0] = TriMA_genOnArray(price,period,bar); break;
   case 19: ma[index][0] = VWMAOnArray(price,period,bar); break;
   case 20: ma[index][0] = JSmoothOnArray(index,0,price[bar],period,1,cbars,bar); break;
   case 21: ma[index][0] = SMA_eqOnArray(price,MA,period,cbars,bar); break;
   case 22: ma[index][0] = ALMAOnArray(price,period,0.85,8,bar); break;
   case 23: ma[index][0] = TEMAOnArray(index,price[bar],period,1,cbars,bar); break;
   case 24: ma[index][0] = T3OnArray(index,0,price[bar],period,0.7,cbars,bar); break;
   case 25: ma[index][0] = LaguerreOnArray(index,price[bar],period,4,cbars,bar); break;
   case 26: ma[index][0] = McGinleyOnArray(price[bar],ma[index][1],period,cbars,bar); break;
   case 27: ma[index][0] = BF2POnArray(price,MA,period,cbars,bar); break;
   case 28: ma[index][0] = BF3POnArray(price,MA,period,cbars,bar); break;
   case 29: ma[index][0] = SuperSmuOnArray(price,MA,period,cbars,bar); break;
   default: ma[index][0] = SMAOnArray(price,period,bar); break;
   }
   
   return(ma[index][0]);
}

// MA_Method=0: SMA - Simple Moving Average
double SMAOnArray(double& array[],int per,int bar)
{
   double sum = 0;
   for(int i=0;i<per;i++) sum += array[bar+i];
   
   return(sum/per);
}                
// MA_Method=1: EMA - Exponential Moving Average
double EMAOnArray(double price,double prev,int per,int cbars,int bar)
{
   if(bar >= cbars - 2) double ema = price;
   else 
   ema = prev + 2.0/(1 + per)*(price - prev); 
   
   return(ema);
}
// MA_Method=2: Wilder - Wilder Exponential Moving Average
double WilderOnArray(double price,double prev,int per,int cbars,int bar)
{
   if(bar >= cbars - 2) double wilder = price; 
   else 
   wilder = prev + (price - prev)/per; 
   
   return(wilder);
}

// MA_Method=3: LWMA - Linear Weighted Moving Average 
double LWMAOnArray(double& array[],int per,int bar)
{
   double sum = 0, weight = 0;
   
      for(int i=0;i<per;i++)
      { 
      weight += (per - i);
      sum    += array[bar+i]*(per - i);
      }
   
   if(weight > 0) return(sum/weight); else return(0); 
} 

// MA_Method=4: SineWMA - Sine Weighted Moving Average
double SineWMAOnArray(double& array[],int per,int bar)
{
   double sum = 0, weight = 0;
  
      for(int i=0;i<per;i++)
      { 
      weight += MathSin(pi*(i + 1)/(per + 1));
      sum    += array[bar+i]*MathSin(pi*(i + 1)/(per + 1)); 
      }
   
   if(weight > 0) return(sum/weight); else return(0); 
}

// MA_Method=5: TriMA - Triangular Moving Average
double TriMAOnArray(double& array[],int per,int bar)
{
   int len = MathCeil((per + 1)*0.5);
   double sum = 0;
   
   for(int i=0;i<len;i++) sum += SMAOnArray(array,len,bar+i);
         
   return(sum/len);
}

// MA_Method=6: LSMA - Least Square Moving Average (or EPMA, Linear Regression Line)
double LSMAOnArray(double& array[],int per,int bar)
{   
   double sum = 0;
   
   for(int i=per;i>=1;i--) sum += (i - (per + 1)/3.0)*array[bar+per-i];
   
   return(sum*6/(per*(per + 1)));
}

// MA_Method=7: SMMA - Smoothed Moving Average
double SMMAOnArray(double& array[],double prev,int per,int cbars,int bar)
{
   if(bar == cbars - per) double smma = SMAOnArray(array,per,bar);
   else 
   if(bar  < cbars - per)
   {
   double sum = 0;
   for(int i=0;i<per;i++) sum += array[bar+i+1];
   smma = (sum - prev + array[bar])/per;
   }
   
   return(smma);
}                

// MA_Method=8: HMA - Hull Moving Average by Alan Hull
double HMAOnArray(double& array[],int per,int cbars,int bar)
{
   double _tmp[];
   int len = MathSqrt(per);
   
   ArrayResize(_tmp,len);
   
   if(bar == cbars - per) double hma = array[bar]; 
   else
   if(bar < cbars - per)
   {
   for(int i=0;i<len;i++) _tmp[i] = 2*LWMAOnArray(array,per/2,bar+i) - LWMAOnArray(array,per,bar+i);  
   hma = LWMAOnArray(_tmp,len,0); 
   }  

   return(hma);
}

// MA_Method=9: ZeroLagEMA - Zero-Lag Exponential Moving Average
double ZeroLagEMAOnArray(double& price[],double prev,int per,int cbars,int bar)
{
   int lag = 0.5*(per - 1); 
   double alpha = 2.0/(1 + per); 
      
   if(bar >= cbars - lag) double zema = price[bar];
   else 
   zema = alpha*(2*price[bar] - price[bar+lag]) + (1 - alpha)*prev;
   
   return(zema);
}

// MA_Method=10: DEMA - Double Exponential Moving Average by Patrick Mulloy
double DEMAOnArray(int index,int num,double price,double per,double v,int cbars,int bar)
{
   double alpha = 2.0/(1 + per);
   
   if(bar == cbars - 2) {double dema = price; tmp[num][index][0] = dema; tmp[num+1][index][0] = dema;}
   else 
   if(bar <  cbars - 2) 
   {
   tmp[num  ][index][0] = tmp[num  ][index][1] + alpha*(price              - tmp[num  ][index][1]); 
   tmp[num+1][index][0] = tmp[num+1][index][1] + alpha*(tmp[num][index][0] - tmp[num+1][index][1]); 
   dema                 = tmp[num  ][index][0]*(1+v) - tmp[num+1][index][0]*v;
   }
   
   return(dema);
}

// MA_Method=11: T3 by T.Tillson
double T3_basicOnArray(int index,int num,double price,int per,double v,int cbars,int bar)
{
   double dema1, dema2;
   
   if(bar == cbars - 2) 
   {
   double T3 = price; 
   for(int k=0;k<6;k++) tmp[num+k][index][0] = price;
   }
   else 
   if(bar < cbars - 2) 
   {
   dema1 = DEMAOnArray(index,num  ,price,per,v,cbars,bar); 
   dema2 = DEMAOnArray(index,num+2,dema1,per,v,cbars,bar); 
   T3    = DEMAOnArray(index,num+4,dema2,per,v,cbars,bar);
   }
   
   return(T3);
}

// MA_Method=12: ITrend - Instantaneous Trendline by J.Ehlers
double ITrendOnArray(double& price[],double& array[],int per,int cbars,int bar)
{
   double alpha = 2.0/(per + 1);
   if(bar < cbars - 7) double it = (alpha - 0.25*alpha*alpha)*price[bar] + 0.5*alpha*alpha*price[bar+1] 
                                 - (alpha - 0.75*alpha*alpha)*price[bar+2] + 2*(1 - alpha)*array[1] 
                                 - (1 - alpha)*(1 - alpha)*array[2];
   else it = (price[bar] + 2*price[bar+1] + price[bar+2])/4;
   
   return(it);
}
// MA_Method=13: Median - Moving Median
double MedianOnArray(double& price[],int per,int bar)
{
   double array[];
   ArrayResize(array,per);
   
   for(int i=0;i<per;i++) array[i] = price[bar+i];
   ArraySort(array,WHOLE_ARRAY,0,MODE_DESCEND);
   
   int num = MathRound((per - 1)*0.5); 
   if(MathMod(per,2) > 0) double median = array[num]; else median = 0.5*(array[num] + array[num+1]);
    
   return(median); 
}

// MA_Method=14: GeoMean - Geometric Mean
double GeoMeanOnArray(double& price[],int per,int cbars,int bar)
{
   if(bar < cbars - per)
   { 
   double gmean = MathPow(price[bar],1.0/per); 
   for(int i=1;i<per;i++) gmean *= MathPow(price[bar+i],1.0/per); 
   }
   else gmean = SMAOnArray(price,per,bar);
   
   return(gmean);
}

// MA_Method=15: REMA - Regularized EMA by Chris Satchwell 
double REMAOnArray(double price,double& array[],int per,double lambda,int cbars,int bar)
{
   double alpha =  2.0/(per + 1);
   
   if(bar >= cbars - 3) double rema = price;
   else 
   rema = (array[1]*(1 + 2*lambda) + alpha*(price - array[1]) - lambda*array[2])/(1 + lambda); 
   
   return(rema);
}
// MA_Method=16: ILRS - Integral of Linear Regression Slope 
double ILRSOnArray(double& price[],int per,int bar)
{
   double sum  = per*(per - 1)*0.5;
   double sum2 = (per - 1)*per*(2*per - 1)/6.0;
     
   double sum1 = 0;
   double sumy = 0;
      for(int i=0;i<per;i++)
      { 
      sum1 += i*price[bar+i];
      sumy += price[bar+i];
      }
   double num1 = per*sum1 - sum*sumy;
   double num2 = sum*sum - per*sum2;
   
   if(num2 != 0) double slope = num1/num2; else slope = 0; 
   double ilrs = slope + SMAOnArray(price,per,bar);
   
   return(ilrs);
}
// MA_Method=17: IE/2 - Combination of LSMA and ILRS 
double IE2OnArray(double& price[],int per,int bar)
{
   double ie = 0.5*(ILRSOnArray(price,per,bar) + LSMAOnArray(price,per,bar));
      
   return(ie); 
}
 
// MA_Method=18: TriMAgen - Triangular Moving Average Generalized by J.Ehlers
double TriMA_genOnArray(double& array[],int per,int bar)
{
   int len1 = MathFloor((per + 1)*0.5);
   int len2 = MathCeil ((per + 1)*0.5);
   double sum = 0;
   
   for(int i = 0;i < len2;i++) sum += SMAOnArray(array,len1,bar+i);
   
   return(sum/len2);
}

// MA_Method=19: VWMA - Volume Weighted Moving Average 
double VWMAOnArray(double& array[],int per,int bar)
{
   double sum = 0, weight = 0;
   
      for(int i=0;i<per;i++)
      { 
      weight += Volume[bar+i];
      sum    += array[bar+i]*Volume[bar+i];
      }
   
   if(weight > 0) return(sum/weight); else return(0); 
} 

// MA_Method=20: JSmooth - Smoothing by Mark Jurik
double JSmoothOnArray(int index,int num,double price,int per,double power,int cbars,int bar)
{
   double beta  = 0.45*(per - 1)/(0.45*(per - 1) + 2);
	double alpha = MathPow(beta,power);
	
	if(bar == cbars - 2) {tmp[num+4][index][0] = price; tmp[num+0][index][0] = price; tmp[num+2][index][0] = price;}
	else 
   if(bar <  cbars - 2) 
   {
	tmp[num+0][index][0] = (1 - alpha)*price + alpha*tmp[num+0][index][1];
	tmp[num+1][index][0] = (price - tmp[num+0][index][0])*(1 - beta) + beta*tmp[num+1][index][1];
	tmp[num+2][index][0] = tmp[num+0][index][0] + tmp[num+1][index][0];
	tmp[num+3][index][0] = (tmp[num+2][index][0] - tmp[num+4][index][1])*MathPow((1-alpha),2) + MathPow(alpha,2)*tmp[num+3][index][1];
	tmp[num+4][index][0] = tmp[num+4][index][1] + tmp[num+3][index][0]; 
   }
   return(tmp[num+4][index][0]);
}

// MA_Method=21: SMA_eq     - Simplified SMA
double SMA_eqOnArray(double& price[],double& array[],int per,int cbars,int bar)
{
   if(bar == cbars - per) double sma = SMAOnArray(price,per,bar);
   else 
   if(bar <  cbars - per) sma = (price[bar] - price[bar+per])/per + array[1]; 
   
   return(sma);
}                        		

// MA_Method=22: ALMA by Arnaud Legoux / Dimitris Kouzis-Loukas / Anthony Cascino
double ALMAOnArray(double& price[],int per,double offset,double sigma,int bar)
{
   double m = MathFloor(offset*(per - 1)), s = per/sigma, w, sum = 0, wsum = 0;		
	
	for (int i=0;i<per;i++) 
	{
	w     = MathExp(-((i - m)*(i - m))/(2*s*s));
   wsum += w;
   sum  += price[bar+(per-1-i)]*w; 
   }
   
   if(wsum != 0) return(sum/wsum); else return(0);
}   

// MA_Method=23: TEMA - Triple Exponential Moving Average by Patrick Mulloy
double TEMAOnArray(int index,double price,int per,double v,int cbars,int bar)
{
   double alpha = 2.0/(per+1);
	
	if(bar == cbars - 2) {tmp[0][index][0] = price; tmp[1][index][0] = price; tmp[2][index][0] = price;}
	else 
   if(bar <  cbars - 2) 
   {
	tmp[0][index][0] = tmp[0][index][1] + alpha *(price            - tmp[0][index][1]);
	tmp[1][index][0] = tmp[1][index][1] + alpha *(tmp[0][index][0] - tmp[1][index][1]);
	tmp[2][index][0] = tmp[2][index][1] + alpha *(tmp[1][index][0] - tmp[2][index][1]);
	tmp[3][index][0] = tmp[0][index][0] + v*(tmp[0][index][0] + v*(tmp[0][index][0]-tmp[1][index][0]) - tmp[1][index][0] - v*(tmp[1][index][0] - tmp[2][index][0])); 
	}
   
   return(tmp[3][index][0]);
}

// MA_Method=24: T3 by T.Tillson (correct version) 
double T3OnArray(int index,int num,double price,int per,double v,int cbars,int bar)
{
   double len = MathMax((per + 5.0)/3.0 - 1,1), dema1, dema2;
   
   if(bar == cbars - 2) 
   {
   double T3 = price; 
   for(int k=0;k<6;k++) tmp[num+k][index][0] = T3;
   }
   else 
   if(bar < cbars - 2) 
   {
   dema1 = DEMAOnArray(index,num  ,price,len,v,cbars,bar); 
   dema2 = DEMAOnArray(index,num+2,dema1,len,v,cbars,bar); 
   T3    = DEMAOnArray(index,num+4,dema2,len,v,cbars,bar);
   }
      
   return(T3);
}

// MA_Method=25: Laguerre filter by J.Ehlers
double LaguerreOnArray(int index,double price,int per,int order,int cbars,int bar)
{
   double gamma = 1 - 10.0/(per + 9);
   double aPrice[];
   
   ArrayResize(aPrice,order);
   
   for(int i=0;i<order;i++)
   {
      if(bar >= cbars - order) tmp[i][index][0] = price;
      else
      {
         if(i == 0) tmp[i][index][0] = (1 - gamma)*price + gamma*tmp[i][index][1];
         else
         tmp[i][index][0] = -gamma * tmp[i-1][index][0] + tmp[i-1][index][1] + gamma * tmp[i][index][1];
      
      aPrice[i] = tmp[i][index][0];
      }
   }
   double laguerre = TriMA_genOnArray(aPrice,order,0);  

   return(laguerre);
}

// MA_Method=26:  MD - McGinley Dynamic
double McGinleyOnArray(double price,double prev,int per,int cbars,int bar)
{
   if(bar == cbars - 2) double md = price;
   else 
   if(bar <  cbars - 2) 
      if(prev != 0) md = prev + (price - prev)/(per*MathPow(price/prev,4)/2); 
      else md = price;

   return(md);
}

// MA_Method=27: BF2P - Two-pole modified Butterworth filter
double BF2POnArray(double& price[],double& array[],int per,int cbars,int bar)
{
   double a  = MathExp(-1.414*pi/per);
   double b  = 2*a*MathCos(1.414*1.25*pi/per);
   double c2 = b;
   double c3 = -a*a;
   double c1 = 1 - c2 - c3;
   
   if(bar < cbars - 7) double bf2p = c1*(price[bar] + 2*price[bar+1] + price[bar+2])/4 + c2*array[1] + c3*array[2];
   else bf2p = (price[bar] + 2*price[bar+1] + price[bar+2])/4;
   
   return(bf2p);
}

// MA_Method=28: BF3P - Three-pole modified Butterworth filter
double BF3POnArray(double& price[],double& array[],int per,int cbars,int bar)
{
   double a  = MathExp(-pi/per);
   double b  = 2*a*MathCos(1.738*pi/per);
   double c  = a*a;
   double d2 = b + c;
   double d3 = -(c + b*c);
   double d4 = c*c;
   double d1 = 1 - d2 - d3 - d4;
   
   if(bar < cbars - 10) double bf3p = d1*(price[bar] + 3*price[bar+1] + 3*price[bar+2] + price[bar+3])/8 + d2*array[1] + d3*array[2] + d4*array[3];
   else bf3p = (price[bar] + 3*price[bar+1] + 3*price[bar+2] + price[bar+3])/8;
   
   return(bf3p);
}

// MA_Method=29: SuperSmu - SuperSmoother filter
double SuperSmuOnArray(double& price[],double& array[],int per,int cbars,int bar)
{
   double a  = MathExp(-1.414*pi/per);
   double b  = 2*a*MathCos(1.414*pi/per);
   double c2 = b;
   double c3 = -a*a;
   double c1 = 1 - c2 - c3;
      
   if(bar < cbars - 7) double supsm = c1*(price[bar] + price[bar+1])/2 + c2*array[1] + c3*array[2];
   else supsm = (price[bar] + price[bar+1])/2;
   
   return(supsm);
}

// HeikenAshi 
double   haClose[2], haOpen[2], haHigh[2], haLow[2];
datetime prevhatime;

double HeikenAshi(int price,double close,double high,double low,double open,int cbars,int bar,double& haH,double& haL)
{ 
   if(prevhatime != Time[bar])
   {
   haClose[1] = haClose[0];
   haOpen [1] = haOpen [0];
   haHigh [1] = haHigh [0];
   haLow  [1] = haLow  [0];
   prevhatime = Time[bar];
   }
  
   if(bar == cbars - 1) 
   {
   haClose[0] = close;
   haOpen [0] = open;
   haHigh [0] = high;
   haLow  [0] = low;
   }
   else
   if(bar < cbars) 
   {
   haClose[0] = (open + high + low + close)/4;
   haOpen [0] = (haOpen[1] + haClose[1])/2;
   haHigh [0] = MathMax(high,MathMax(haOpen[0],haClose[0]));
   haLow  [0] = MathMin(low ,MathMin(haOpen[0],haClose[0]));
   }
   
   haL = haLow[0];
   haH = haHigh[0];
   
   switch(price)
   {
   case  0: return(haClose[0]); break;
   case  1: return(haOpen [0]); break;
   case  2: return(haHigh [0]); break;
   case  3: return(haLow  [0]); break;
   case  4: return((haHigh [0] + haLow [0])/2); break;
   case  5: return((haHigh[0] + haLow[0] +   haClose[0])/3); break;
   case  6: return((haHigh[0] + haLow[0] + 2*haClose[0])/4); break;
   default: return(haClose[0]); break;
   }
}

double getPrice(int price,int bar)
{
   double avgClose = Close[bar];
   double avgOpen  = Open [bar];
   double avgHigh  = High [bar];
   double avgLow   = Low  [bar];
   
   switch(price)
   {   
   case  0: return(avgClose); break;
   case  1: return(avgOpen ); break;
   case  2: return(avgHigh ); break;
   case  3: return(avgLow  ); break;
   case  4: return((avgHigh  + avgLow )/2); break;
   case  5: return((avgHigh  + avgLow +   avgClose)/3); break;
   case  6: return((avgHigh  + avgLow + 2*avgClose)/4); break;
   default: return(avgClose); break;
   }
}


datetime prevnbtime;

bool isNewBar(int tf)
{
   bool res = false;
   
   if(tf >= 0)
   {
      if(iTime(NULL,tf,0) != prevnbtime)
      {
      res   = true;
      prevnbtime = iTime(NULL,tf,0);
      }   
   }
   else res = true;
   
   return(res);
}

string prevmess;
 
bool BoxAlert(bool cond,string text)   
{      
   string mess = IndicatorName + "("+Symbol()+","+TF + ")" + text;
   
   if (cond && mess != prevmess)
	{
	Alert (mess);
	prevmess = mess; 
	return(true);
	} 
  
   return(false);  
}

datetime pausetime;

bool Pause(int sec)
{
   if(TimeCurrent() >= pausetime + sec) {pausetime = TimeCurrent(); return(true);}
   
   return(false);
}

datetime warningtime;

void WarningSound(bool cond,int num,int sec,string sound,datetime curtime)
{
   static int i;
   
   if(cond)
   {
   if(curtime != warningtime) i = 0; 
   if(i < num && Pause(sec)) {PlaySound(sound); warningtime = curtime; i++;}       	
   }
}

string prevemail;

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 prevpush;
 
bool PushAlert(bool cond,string text)   
{      
   string push = IndicatorName + "("+Symbol() + "," + TF + ")" + text;
   
   if(cond && push != prevpush)
	{
	SendNotification(push);
	
	prevpush = push; 
	return(true);
	} 
  
   return(false);  
}



string tf(int itimeframe)
{
   string result = "";
   
   switch(itimeframe)
   {
   case PERIOD_M1:   result = "M1" ;
   case PERIOD_M5:   result = "M5" ;
   case PERIOD_M15:  result = "M15";
   case PERIOD_M30:  result = "M30";
   case PERIOD_H1:   result = "H1" ;
   case PERIOD_H4:   result = "H4" ;
   case PERIOD_D1:   result = "D1" ;
   case PERIOD_W1:   result = "W1" ;
   case PERIOD_MN1:  result = "MN1";
   default:          result = "N/A";
   }
   
   if(result == "N/A")
   {
   if(itimeframe <  PERIOD_H1 ) result = "M"  + itimeframe;
   if(itimeframe >= PERIOD_H1 ) result = "H"  + itimeframe/PERIOD_H1;
   if(itimeframe >= PERIOD_D1 ) result = "D"  + itimeframe/PERIOD_D1;
   if(itimeframe >= PERIOD_W1 ) result = "W"  + itimeframe/PERIOD_W1;
   if(itimeframe >= PERIOD_MN1) result = "MN" + itimeframe/PERIOD_MN1;
   }
   
   return(result); 
} 

void SetBox(string name,int window,datetime time1,double price1, datetime time2,double price2,bool fill,color bg_color)
{
   ObjectDelete(0,name);
   
   if(ObjectCreate(0,name,OBJ_RECTANGLE,window,time1,price1,time2,price2))
   {
   ObjectSetInteger(0,name,OBJPROP_STYLE      , STYLE_SOLID);
   ObjectSetInteger(0,name,OBJPROP_BACK       ,        true);
   ObjectSetInteger(0,name,OBJPROP_SELECTABLE ,           0);
   ObjectSetInteger(0,name,OBJPROP_SELECTED   ,           0);
   ObjectSetInteger(0,name,OBJPROP_HIDDEN     ,        true);
   ObjectSetInteger(0,name,OBJPROP_ZORDER     ,           0);
   ObjectSetInteger(0,name,OBJPROP_FILL       ,        fill);
   ObjectSetInteger(0,name,OBJPROP_COLOR      ,    bg_color);
   }
}

bool deleteObj(string name)
{
   bool result = false;
   
   int length = StringLen(name);
   for(int i=ObjectsTotal()-1; i>=0; i--)
   {
   string objName = ObjectName(i); 
   if(StringSubstr(objName,0,length) == name) {ObjectDelete(objName); result = true;}
   }
   
   return(result);
}
                 