//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1  clrLimeGreen
#property indicator_color2  clrPaleVioletRed
#property indicator_color3  clrLimeGreen
#property indicator_color4  clrPaleVioletRed
#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;   // Time frame
extern bool            UseAutomaticOmaPeriods = false;          // Use automatic settings?
extern int             OmaPeriod              = 20;             // Oma period
extern double          OmaSpeed               = 3.0;            // Oma speed
extern bool            OmaAdaptive            = true;           // Oma adaptive
input int              inpWickWidth           = 1;                // Wick width (If auto candle = false)
input int              inpBodyWidth           = 3;                // Body width (If auto candle = false)
extern bool            alertsOn               = false;          // Alerts on?
extern bool            alertsOnBody           = true;           // Alerts on body change?
extern bool            alertsOnWick           = false;          // Alerts on wick change?
extern bool            alertsOnCurrent        = false;          // Alerts on current?
extern bool            alertsMessage          = true;           // Alerts message?
extern bool            alertsNotification     = false;          // Alerts push notification?
extern bool            alertsSound            = false;          // Alerts sound?
extern bool            alertsEmail            = false;          // Alerts email?
extern bool            Interpolate            = true;           // Interpolate in multi time frame mode?

//
//
//
//
//

double upW[],dnW[],upB[],dnB[],hahu[],hahd[],hahbu[],hahbd[],trendw[],trendb[],count[];
string indicatorFileName;
#define _mtfCall(_buf,_ind) iCustom(NULL,TimeFrame,indicatorFileName,0,UseAutomaticOmaPeriods,OmaPeriod,OmaSpeed,OmaAdaptive,inpWickWidth,inpBodyWidth,alertsOn,alertsOnBody,alertsOnWick,alertsOnCurrent,alertsMessage,alertsNotification,alertsSound,alertsEmail,_buf,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//

int init()
{
   IndicatorBuffers(11);
   SetIndexBuffer(0, upW,INDICATOR_DATA); SetIndexStyle(0,DRAW_HISTOGRAM,EMPTY,inpWickWidth);
   SetIndexBuffer(1, dnW,INDICATOR_DATA); SetIndexStyle(1,DRAW_HISTOGRAM,EMPTY,inpWickWidth);
   SetIndexBuffer(2, upB,INDICATOR_DATA); SetIndexStyle(2,DRAW_HISTOGRAM,EMPTY,inpBodyWidth);
   SetIndexBuffer(3, dnB,INDICATOR_DATA); SetIndexStyle(3,DRAW_HISTOGRAM,EMPTY,inpBodyWidth);
   SetIndexBuffer(4, hahd); 
   SetIndexBuffer(5, hahu); 
   SetIndexBuffer(6, hahbd);
   SetIndexBuffer(7, hahbu);
   SetIndexBuffer(8,trendw);
   SetIndexBuffer(9,trendb);
   SetIndexBuffer(10,count);
   OmaPeriod = fmax(OmaPeriod,1);
   OmaSpeed  = fmax(OmaSpeed,-1.5);
      if (UseAutomaticOmaPeriods)
         switch(_Period) 
         {
            case PERIOD_M1:  OmaPeriod = 150; break;
            case PERIOD_M5:  OmaPeriod =  60; break;
            case PERIOD_M15: OmaPeriod =  80; break;
            case PERIOD_M30: OmaPeriod = 192; break;
            case PERIOD_H1:  OmaPeriod =  96; break;
            case PERIOD_H4:  OmaPeriod = 120; break;
            default :        OmaPeriod = 80;
         }
         
   
      //
      //
      //
      //
      //
      
         indicatorFileName = WindowExpertName();
        TimeFrame         = (enTimeFrames)timeFrameValue(TimeFrame);
         IndicatorSetDouble(INDICATOR_MINIMUM,0);
         IndicatorSetDouble(INDICATOR_MAXIMUM,2);
         
         IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(TimeFrame)+"  OMA Smoothed");
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); count[0] = limit;
            if (TimeFrame != _Period)
            {
               limit = (int)MathMax(limit,MathMin(Bars-1,_mtfCall(6,0)*TimeFrame/Period()));
               for (int i=limit; i>=0; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                     upW[i] = _mtfCall(0,y);
                     dnW[i] = _mtfCall(1,y);
                     upB[i] = _mtfCall(2,y);
                     dnB[i] = _mtfCall(3,y);     
               }
      return(0);
      }

      //
      //
      //
      //
      //
   
      for (int i=limit; i>=0; i--)
      {
         double haHigh,haLow,haOpen,haClose;
            calculateHA(OmaPeriod,OmaSpeed,OmaAdaptive,haOpen,haClose,haHigh,haLow,i);
            
            hahu[i]  = haHigh; 
            hahd[i]  = haLow;
            hahbu[i] = haOpen;
            hahbd[i] = haClose;
                     trendb[i] = (i<Bars-1) ? trendb[i+1] : 0;
                     trendw[i] = (i<Bars-1) ? trendw[i+1] : 0;
                     if (hahu[i] <hahd[i])  trendw[i] =  1;
                     if (hahu[i] >hahd[i])  trendw[i] = -1;
                     if (hahbu[i]<hahbd[i]) trendb[i] =  1;
                     if (hahbu[i]>hahbd[i]) trendb[i] = -1;
                     upW[i]   = (trendw[i] ==  1) ? 2 : EMPTY_VALUE;
                     dnW[i]   = (trendw[i] == -1) ? 2 : EMPTY_VALUE;
                     upB[i]   = (trendb[i] ==  1) ? 1 : EMPTY_VALUE;
                     dnB[i]   = (trendb[i] == -1) ? 1 : EMPTY_VALUE;
      }            
      manageAlerts();
      return(0);      
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//

#define _haInstances     1
#define _haInstancesSize 4
double workHa[][_haInstances*_haInstancesSize];
#define _haH 0
#define _haL 1
#define _haO 2
#define _haC 3

void calculateHA(int omaPeriod, double Speed, bool Adaptive, double& tOpen, double& tClose, double& tHigh, double& tLow, int i, int instanceNo=0)
{
   if (ArrayRange(workHa,0)!= Bars) ArrayResize(workHa,Bars); int r=Bars-i-1; instanceNo*=_haInstancesSize;
   
   //
   //
   //
   //
   //
   
      double maOpen  = iAverage(Open[i], omaPeriod,Speed,Adaptive,i,0);
      double maClose = iAverage(Close[i],omaPeriod,Speed,Adaptive,i,1);
      double maLow   = iAverage(Low[i],  omaPeriod,Speed,Adaptive,i,2);
      double maHigh  = iAverage(High[i], omaPeriod,Speed,Adaptive,i,3);
      double haOpen  = (r>0) ? (workHa[r-1][instanceNo+_haO] + workHa[r-1][instanceNo+_haC])/2.0 : maOpen;
      double haClose = (maOpen+maHigh+maLow+maClose)/4;
      double haHigh  = MathMax(maHigh, MathMax(haOpen, haClose));
      double haLow   = MathMin(maLow,  MathMin(haOpen, haClose));

      if(haOpen<haClose) { workHa[r][instanceNo+_haH] = haLow;  workHa[r][instanceNo+_haL] = haHigh; } 
      else               { workHa[r][instanceNo+_haH] = haHigh; workHa[r][instanceNo+_haL] = haLow;  } 
                           workHa[r][instanceNo+_haO] = haOpen;
                           workHa[r][instanceNo+_haC] = haClose;
      //
      //
      //
      //
      //
      
   tHigh  = workHa[r][instanceNo+_haH];
   tLow   = workHa[r][instanceNo+_haL];
   tOpen  = workHa[r][instanceNo+_haO];
   tClose = workHa[r][instanceNo+_haC];
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#define omaInstances 4
double workOma[][omaInstances*7];
#define E1  0
#define E2  1
#define E3  2
#define E4  3
#define E5  4
#define E6  5
#define res 6

//
//
//
//
//

double iAverage(double price, double averagePeriod, double tconst, bool adaptive, int i, int instanceNo=0)
{
   if (averagePeriod <=1) return(price); int r = Bars-i-1; instanceNo *= 7;
   if (ArrayRange(workOma,0)!=Bars) ArrayResize(workOma,Bars);
   
   double e1=workOma[(int)fmax(r-1,0)][instanceNo+E1];  double e2=workOma[(int)fmax(r-1,0)][instanceNo+E2];
   double e3=workOma[(int)fmax(r-1,0)][instanceNo+E3];  double e4=workOma[(int)fmax(r-1,0)][instanceNo+E4];
   double e5=workOma[(int)fmax(r-1,0)][instanceNo+E5];  double e6=workOma[(int)fmax(r-1,0)][instanceNo+E6];

   //
   //
   //
   //
   //

      if (adaptive && (averagePeriod > 1))
      {
         double minPeriod = averagePeriod/2.0;
         double maxPeriod = minPeriod*5.0;
         int    endPeriod = (int)MathCeil(maxPeriod);
         double signal    = fabs((price-workOma[(int)fmax(r-endPeriod,0)][instanceNo+res]));
         double noise     = 0.00000000001;

            for(int k=1; k<endPeriod && r-k>=0; k++) noise=noise+fabs(price-workOma[r-k][instanceNo+res]);

         averagePeriod = ((signal/noise)*(maxPeriod-minPeriod))+minPeriod;
      }
      
      //
      //
      //
      //
      //
      
      double alpha = (2.0+tconst)/(1.0+tconst+averagePeriod);

      e1 = e1 + alpha*(price-e1); e2 = e2 + alpha*(e1-e2); double v1 = 1.5 * e1 - 0.5 * e2;
      e3 = e3 + alpha*(v1   -e3); e4 = e4 + alpha*(e3-e4); double v2 = 1.5 * e3 - 0.5 * e4;
      e5 = e5 + alpha*(v2   -e5); e6 = e6 + alpha*(e5-e6); double v3 = 1.5 * e5 - 0.5 * e6;

   //
   //
   //
   //
   //

   workOma[r][instanceNo+E1]  = e1;  workOma[r][instanceNo+E2] = e2;
   workOma[r][instanceNo+E3]  = e3;  workOma[r][instanceNo+E4] = e4;
   workOma[r][instanceNo+E5]  = e5;  workOma[r][instanceNo+E6] = e6;
   workOma[r][instanceNo+res] = price;
   return(v3);
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------

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;
      
      //
      //
      //
      //
      //
      
      static datetime prevTime1  = 0;
      static string   prevAlert1 = "";
         if (alertsOnBody && trendb[whichBar] != trendb[whichBar+1])
         {
            if (trendb[whichBar] ==  1) doAlert(prevTime1,prevAlert1," Main HA trend changed to up");
            if (trendb[whichBar] == -1) doAlert(prevTime1,prevAlert1," Main HA trend changed to down");
         }
      static datetime prevTime2  = 0;
      static string   prevAlert2 = "";
         if (alertsOnWick && trendw[whichBar] != trendw[whichBar+1])
         {
            if (trendw[whichBar] ==  1) doAlert(prevTime2,prevAlert2," HA wick trend changed to up");
            if (trendw[whichBar] == -1) doAlert(prevTime2,prevAlert2," HA wick 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 =  Symbol()+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+doWhat;
          if (alertsMessage)      Alert(message);
          if (alertsNotification) SendNotification(message);
          if (alertsEmail)        SendMail(StringConcatenate(Symbol(),"HA smoothed 3 "),message);
          if (alertsSound)        PlaySound("alert2.wav");
   }
}


//
 