//------------------------------------------------------------------
//
//   divisor modification idea by SwingMan
//
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"
//------------------------------------------------------------------
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrDeepSkyBlue
#property indicator_color2 clrSandyBrown
#property indicator_color3 clrSandyBrown
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property strict
//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame       = PERIOD_CURRENT; // Time frame
extern int                HMAPeriod       = 27;             // Hull period
extern ENUM_APPLIED_PRICE HMAPrice        = PRICE_CLOSE;    // Price to use
extern double             HMADivisor      = 1.5;            // Hull divisor (2 for original Hull average)
extern bool               AlertsOn        = false;          // Turn alerts on?
extern bool               AlertsOnCurrent = true;           // Alerts on current (still opened) bar?
extern bool               AlertsMessage   = true;           // Alerts should display message?
extern bool               AlertsSound     = false;          // Alerts should play alert sound?
extern bool               AlertsEmail     = false;          // Alerts should send email?
extern int                Shift           = 0;              // Hull shift
extern bool               Interpolate     = true;           // Interpolate in multi time frame mode

//
//
//
//
//

double hma[];
double hmaDa[];
double hmaDb[];
double trend[];

string indicatorFileName;
bool   returnBars;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(4);
   SetIndexBuffer(0,hma);
   SetIndexBuffer(1,hmaDa);
   SetIndexBuffer(2,hmaDb);
   SetIndexBuffer(3,trend);

      //
      //
      //
      //
      //
      
         indicatorFileName = WindowExpertName();
         returnBars        = TimeFrame==-99; 
         TimeFrame         = MathMax(TimeFrame,_Period);
         for (int i=0; i<7; i++) SetIndexShift(i,Shift*TimeFrame/Period());
   IndicatorShortName(timeFrameToString(TimeFrame)+" Hull variation ("+(string)HMAPeriod+")");
   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);
           if (returnBars) { hma[0] = MathMin(limit+1,Bars-1); return(0); }

   //
   //
   //
   //
   //

   if (TimeFrame == _Period)
   {
      if (trend[limit]==-1) CleanPoint(limit,hmaDa,hmaDb);
      for(int i=limit; i>=0; i--)
      {
         hma[i]   = iHull(iMA(NULL,0,1,0,MODE_SMA,HMAPrice,i),HMAPeriod,HMADivisor,i,0);
         hmaDa[i] = EMPTY_VALUE;
         hmaDb[i] = EMPTY_VALUE;
         if (i<(Bars-1))
         {
            trend[i] = trend[i+1];
               if (hma[i]>hma[i+1]) trend[i] =  1;
               if (hma[i]<hma[i+1]) trend[i] = -1;
               if (trend[i]==-1) PlotPoint(i,hmaDa,hmaDb,hma);
         }               
      }
      manageAlerts();
      return(0);
   }
   
   //
   //
   //
   //
   //
   
   limit = (int)MathMax(limit,MathMin(Bars-1,iCustom(NULL,TimeFrame,indicatorFileName,-99,0,0)*TimeFrame/_Period));
   if (trend[limit]==-1) CleanPoint(limit,hmaDa,hmaDb);
   for (int i=limit; i>=0; i--)
   {
      int y = iBarShift(NULL,TimeFrame,Time[i]);
         hma[i]   = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,HMAPeriod,HMAPrice,HMADivisor,AlertsOn,AlertsOnCurrent,AlertsMessage,AlertsSound,AlertsEmail,0,0,y);
         trend[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,HMAPeriod,HMAPrice,HMADivisor,AlertsOn,AlertsOnCurrent,AlertsMessage,AlertsSound,AlertsEmail,0,3,y);
         hmaDa[i] = EMPTY_VALUE;
         hmaDb[i] = EMPTY_VALUE;

               if (!Interpolate ||  (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;

               //
               //
               //
               //
               //
      
               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+k)<Bars && (i+n)<Bars; k++)
                  hma[i+k] = hma[i] + (hma[i+n] - hma[i])*k/n;
   }
   for (int i=limit;i>=0;i--) if (trend[i]==-1) PlotPoint(i,hmaDa,hmaDb,hma);
   return(0);
}


//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//


double workHull[][4];
double iHull(double price, double period, double divisor, int r, int instanceNo=0)
{
   if (ArrayRange(workHull,0)!= Bars) ArrayResize(workHull,Bars); r=Bars-r-1;

   //
   //
   //
   //
   //

      int HmaPeriod  = (int)MathMax(period,2);
      int HalfPeriod = (int)MathFloor(HmaPeriod/divisor);
      int HullPeriod = (int)MathFloor(MathSqrt(HmaPeriod));
      double thma,hmw,weight; instanceNo *= 2;

         workHull[r][instanceNo] = price;

         //
         //
         //
         //
         //
               
         hmw = HalfPeriod; thma = hmw*price; 
            for(int k=1; k<HalfPeriod && (r-k)>=0; k++)
            {
               weight = HalfPeriod-k;
               hmw   += weight;
               thma  += weight*workHull[r-k][instanceNo];  
            }             
            workHull[r][instanceNo+1] = 2.0*thma/hmw;

         hmw = HmaPeriod; thma = hmw*price; 
            for(int k=1; k<period && (r-k)>=0; k++)
            {
               weight = HmaPeriod-k;
               hmw   += weight;
               thma  += weight*workHull[r-k][instanceNo];
            }             
            workHull[r][instanceNo+1] -= thma/hmw;

         //
         //
         //
         //
         //
         
         hmw = HullPeriod; thma = hmw*workHull[r][instanceNo+1];
            for(int k=1; k<HullPeriod && (r-k)>=0; k++)
            {
               weight = HullPeriod-k;
               hmw   += weight;
               thma   += weight*workHull[r-k][1+instanceNo];  
            }
   return(thma/hmw);
}


//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (AlertsOn)
   {
      int whichBar = 1; if (AlertsOnCurrent) whichBar = 0;
      if (trend[whichBar]!= trend[whichBar+1])
      {
         static datetime time1 = 0;
         static string   mess1 = "";
            if (trend[whichBar] ==  1) doAlert(time1,mess1," hull variation trend changed to up");
            if (trend[whichBar] == -1) doAlert(time1,mess1," hull variation trend changed to down");
      }
   }
}

//
//
//
//
//

void doAlert(datetime& previousTime, string& previousAlert, string doWhat)
{
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[0]) {
       previousAlert  = doWhat;
       previousTime   = Time[0];

       //
       //
       //
       //
       //

       message = timeFrameToString(Period())+" "+Symbol()+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+doWhat;
          if (AlertsMessage) Alert(message);
          if (AlertsEmail)   SendMail(Symbol()+" Hull variation",message);
          if (AlertsSound)   PlaySound("alert2.wav");
   }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

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("");
}