//------------------------------------------------------------------
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"
//------------------------------------------------------------------

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1  clrDodgerBlue
#property indicator_color2  clrSandyBrown
#property indicator_width1  2
#property indicator_width2  2
#property indicator_minimum 0
#property indicator_maximum 1


//
//
//
//
//

enum maType   
{
   ma_sma,  // Simple moving average
   ma_ema,  // Exponential moving average
   ma_smma, // Smoothed moving average
   ma_lwma, // Linear weighted moving average
   ma_t3    // T3
};
extern ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT;
extern int    MaPeriod           = 5;
extern maType MaMetod            = ma_t3;
extern int    Step               = 0;
extern bool   BetterFormula      = true;
extern double T3Hot              = 1.00;
extern bool   T3Original         = false;
extern bool   SortedValues       = true;
extern bool   alertsOn           = false;
extern bool   alertsOnCurrent    = false;
extern bool   alertsMessage      = true;
extern bool   alertsSound        = false;
extern bool   alertsEmail        = false;
extern bool   arrowsVisible      = false;            // Arrows visible?
extern bool   arrowsOnNewest     = false;            // Arrows drawn on newst bar of higher time frame bar?
extern string arrowsIdentifier   = "hama Arrows1";   // Unique ID for arrows
extern double arrowsUpperGap     = 1.0;              // Upper arrow gap
extern double arrowsLowerGap     = 1.0;              // Lower arrow gap
extern color  arrowsUpColor      = clrLimeGreen;     // Up arrow color
extern color  arrowsDnColor      = clrOrange;        // Down arrow color
extern int    arrowsUpCode       = 241;              // Up arrow code
extern int    arrowsDnCode       = 242;              // Down arrow code

//
//
//
//
//

double upH[];
double dnH[];
double Buffer1[];
double Buffer2[];
double Buffer3[];
double Buffer4[];
double UpArrow[];
double DnArrow[];
double trend[];
string indicatorFileName;
bool   returnBars;


//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(8);
   SetIndexBuffer(0, upH); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1, dnH); SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(2, Buffer1); 
   SetIndexBuffer(3, Buffer2); 
   SetIndexBuffer(5, Buffer3); 
   SetIndexBuffer(6, Buffer4); 
   SetIndexBuffer(7, trend);
   
        MaPeriod          = MathMax(1,MaPeriod);
        indicatorFileName = WindowExpertName();
        returnBars        = (TimeFrame==-99);
        TimeFrame         = MathMax(TimeFrame,_Period);  
   
      IndicatorShortName(timeFrameToString(TimeFrame)+"  HAMA T3 Histo");
   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 counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
           int limit=MathMin(Bars-counted_bars,Bars-1);
           int pointModifier = 1;if (Digits==3 || Digits==5) pointModifier = 10;
           if (returnBars) { upH[0] = limit+1; return(0); }

   //
   //
   //
   //
   //

   if (TimeFrame==Period())
   {
     for(int pos=limit; pos >= 0; pos--)
     {
         if (MaMetod==ma_t3)
         {
            double maOpen  = iT3(Open[pos] ,MaPeriod,T3Hot,T3Original,pos,0);
            double maClose = iT3(Close[pos],MaPeriod,T3Hot,T3Original,pos,1);
            double maLow   = iT3(Low[pos]  ,MaPeriod,T3Hot,T3Original,pos,2);
            double maHigh  = iT3(High[pos] ,MaPeriod,T3Hot,T3Original,pos,3);
         }
         else
         {
            maOpen  = iMA(NULL,0,MaPeriod,0,(int)MaMetod,PRICE_OPEN ,pos);
            maClose = iMA(NULL,0,MaPeriod,0,(int)MaMetod,PRICE_CLOSE,pos);
            maLow   = iMA(NULL,0,MaPeriod,0,(int)MaMetod,PRICE_LOW  ,pos);
            maHigh  = iMA(NULL,0,MaPeriod,0,(int)MaMetod,PRICE_HIGH ,pos);
         }
         if (SortedValues)
         {
            double sort[4];
                   sort[0] = maOpen;
                   sort[1] = maClose;
                   sort[2] = maLow;
                   sort[3] = maHigh;
                     ArraySort(sort);
                        maLow  = sort[0];
                        maHigh = sort[3];
                        if (Open[pos]>Close[pos])
                              { maOpen = sort[2]; maClose = sort[1]; }
                        else  { maOpen = sort[1]; maClose = sort[2]; }
         }                        
   
      //
      //
      //
      //
      //
        
         if (BetterFormula) {
               if (maHigh!=maLow)
                     double haClose  = (maOpen+maClose)/2+(((maClose-maOpen)/(maHigh-maLow))*MathAbs((maClose-maOpen)/2));
                     else  haClose   = (maOpen+maClose)/2; }
               else        haClose   = (maOpen+maHigh+maLow+maClose)/4;
                     double haOpen   = (Buffer3[pos+1]+Buffer4[pos+1])/2;
                     double haHigh   = MathMax(maHigh, MathMax(haOpen,haClose));
                     double haLow    = MathMin(maLow,  MathMin(haOpen,haClose));

         if (haOpen<haClose) { Buffer1[pos]=haLow;  Buffer2[pos]=haHigh; } 
         else                { Buffer1[pos]=haHigh; Buffer2[pos]=haLow;  } 
                               Buffer3[pos]=haOpen;
                               Buffer4[pos]=haClose;
         
         //
         //
         //
         //
         //
            
         if (Step>0)
         {
            if( MathAbs(Buffer1[pos]-Buffer1[pos+1]) < Step*pointModifier*Point ) Buffer1[pos]=Buffer1[pos+1];
            if( MathAbs(Buffer2[pos]-Buffer2[pos+1]) < Step*pointModifier*Point ) Buffer2[pos]=Buffer2[pos+1];
            if( MathAbs(Buffer3[pos]-Buffer3[pos+1]) < Step*pointModifier*Point ) Buffer3[pos]=Buffer3[pos+1];
            if( MathAbs(Buffer4[pos]-Buffer4[pos+1]) < Step*pointModifier*Point ) Buffer4[pos]=Buffer4[pos+1];
         }   
         upH[pos]   = EMPTY_VALUE;
         dnH[pos]   = EMPTY_VALUE;      
         trend[pos] = trend[pos+1];
         
         if (Buffer3[pos] < Buffer4[pos]) trend[pos] =  1;
         if (Buffer3[pos] > Buffer4[pos]) trend[pos] = -1;
         if (trend[pos] ==  1) upH[pos] = 1;           
         if (trend[pos] == -1) dnH[pos] = 1;   
         
          //
          //
          //
          //
          //
               
          if (arrowsVisible)
          {
             string lookFor = arrowsIdentifier+":"+(string)Time[pos]; ObjectDelete(lookFor);            
                if (trend[pos] != trend[pos+1])
                {
                   if (trend[pos] == 1) drawArrow(pos,arrowsUpColor,arrowsUpCode,false);
                   if (trend[pos] ==-1) drawArrow(pos,arrowsDnColor,arrowsDnCode, true);
                }
            }          
      }
      
      //
      //
      //
      //
      //
      
      if (alertsOn)
      {
        if (alertsOnCurrent)
           int whichBar = 0;
        else     whichBar = 1; 
        if (trend[whichBar] != trend[whichBar+1])
        {
           if (trend[whichBar] == 1) doAlert(whichBar,"up");
           if (trend[whichBar] ==-1) doAlert(whichBar,"down");
        }         
      }
    return(0);
   }
   
   //
   //
   //
   //
   //
   
   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,TimeFrame,indicatorFileName,-99,0,0)*TimeFrame/Period()));
   for (pos=limit;pos>=0; pos--)
   {
       int y = iBarShift(NULL,TimeFrame,Time[pos]);
          upH[pos] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,MaPeriod,MaMetod,Step,BetterFormula,T3Hot,T3Original,SortedValues,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,arrowsVisible,arrowsOnNewest,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,0,y);
          dnH[pos] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,MaPeriod,MaMetod,Step,BetterFormula,T3Hot,T3Original,SortedValues,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,arrowsVisible,arrowsOnNewest,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,1,y);      
   }
return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workT3[][24];
double workT3Coeffs[][6];
#define _period 0
#define _c1     1
#define _c2     2
#define _c3     3
#define _c4     4
#define _alpha  5

//
//
//
//
//

double iT3(double price, double period, double hot, bool original, int i, int instanceNo=0)
{
   if (ArrayRange(workT3,0) !=Bars)                 ArrayResize(workT3,Bars);
   if (ArrayRange(workT3Coeffs,0) < (instanceNo+1)) ArrayResize(workT3Coeffs,instanceNo+1);

   if (workT3Coeffs[instanceNo][_period] != period)
   {
     workT3Coeffs[instanceNo][_period] = period;
        double a = hot;
            workT3Coeffs[instanceNo][_c1] = -a*a*a;
            workT3Coeffs[instanceNo][_c2] = 3*a*a+3*a*a*a;
            workT3Coeffs[instanceNo][_c3] = -6*a*a-3*a-3*a*a*a;
            workT3Coeffs[instanceNo][_c4] = 1+3*a+a*a*a+3*a*a;
            if (original)
                 workT3Coeffs[instanceNo][_alpha] = 2.0/(1.0 + period);
            else workT3Coeffs[instanceNo][_alpha] = 2.0/(2.0 + (period-1.0)/2.0);
   }
   
   //
   //
   //
   //
   //
   
   int buffer = instanceNo*6;
   int r = Bars-i-1;
   if (r == 0)
      {
         workT3[r][0+buffer] = price;
         workT3[r][1+buffer] = price;
         workT3[r][2+buffer] = price;
         workT3[r][3+buffer] = price;
         workT3[r][4+buffer] = price;
         workT3[r][5+buffer] = price;
      }
   else
      {
         workT3[r][0+buffer] = workT3[r-1][0+buffer]+workT3Coeffs[instanceNo][_alpha]*(price              -workT3[r-1][0+buffer]);
         workT3[r][1+buffer] = workT3[r-1][1+buffer]+workT3Coeffs[instanceNo][_alpha]*(workT3[r][0+buffer]-workT3[r-1][1+buffer]);
         workT3[r][2+buffer] = workT3[r-1][2+buffer]+workT3Coeffs[instanceNo][_alpha]*(workT3[r][1+buffer]-workT3[r-1][2+buffer]);
         workT3[r][3+buffer] = workT3[r-1][3+buffer]+workT3Coeffs[instanceNo][_alpha]*(workT3[r][2+buffer]-workT3[r-1][3+buffer]);
         workT3[r][4+buffer] = workT3[r-1][4+buffer]+workT3Coeffs[instanceNo][_alpha]*(workT3[r][3+buffer]-workT3[r-1][4+buffer]);
         workT3[r][5+buffer] = workT3[r-1][5+buffer]+workT3Coeffs[instanceNo][_alpha]*(workT3[r][4+buffer]-workT3[r-1][5+buffer]);
      }

   //
   //
   //
   //
   //
   
   return(workT3Coeffs[instanceNo][_c1]*workT3[r][5+buffer] + 
          workT3Coeffs[instanceNo][_c2]*workT3[r][4+buffer] + 
          workT3Coeffs[instanceNo][_c3]*workT3[r][3+buffer] + 
          workT3Coeffs[instanceNo][_c4]*workT3[r][2+buffer]);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

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("");
}

//
//
//
//
//

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()," ",timeFrameToString(_Period)," at ",TimeToStr(TimeLocal(),TIME_SECONDS)+" HAMA trend changed to ",doWhat);
             if (alertsMessage) Alert(message);
             if (alertsEmail)   SendMail(StringConcatenate(Symbol(),"HAMA "),message);
             if (alertsSound)   PlaySound("alert2.wav");
      }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void drawArrow(int i,color theColor,int theCode,bool up)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //
      //
      //

      datetime time = Time[i]; if (arrowsOnNewest) time += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,time,0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}