//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"


#property indicator_separate_window
#property indicator_buffers    4

#property indicator_label1     "Force"
#property indicator_type1      DRAW_LINE
#property indicator_color1     clrGray
#property indicator_width1     2 

#property indicator_label2     "Histogram up"
#property indicator_type2      DRAW_HISTOGRAM
#property indicator_color2     clrLimeGreen
#property indicator_width2     2 

#property indicator_style2     STYLE_SOLID
#property indicator_label3     "Histogram down"
#property indicator_type3      DRAW_HISTOGRAM
#property indicator_color3     clrOrange
#property indicator_width3     2 

#property indicator_label4     "Force Signal"
#property indicator_type4      DRAW_LINE
#property indicator_color4     clrRed
#property indicator_width4     2 
#property strict

//
//
//
//
//

enum enMaTypes
{
   ma_sma,     // Simple moving average
   ma_ema,     // Exponential moving average
   ma_smma,    // Smoothed MA
   ma_lwma,    // Linear weighted MA
};

extern ENUM_TIMEFRAMES    TimeFrame         = PERIOD_CURRENT;    // Time frame
input int	              Force_Period   	  = 5;                 // Force period
input ENUM_MA_METHOD      Force_Method	     = 0;                 // Force ma method
input ENUM_APPLIED_PRICE  Force_Price	     = PRICE_CLOSE;       // Force price
input int                 SigLen            = 5;                 // Signal ma period
input enMaTypes           SigMeth           = ma_ema;            // Signal moving average type
input bool                alertsOn          = false;             // Alerts on true/false?
input bool                alertsOnCurrent   = true;              // Alerts on still opened bar true/false?
input bool                alertsMessage     = true;              // Alerts pop-up message true/false?
input bool                alertsSound       = false;             // 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
input bool                arrowsVisible     = false;             // Arrows visible true/false?
input bool                arrowsOnNewest    = false;             // Arrows drawn on newest bar of higher time frame bar true/false?
input string              arrowsIdentifier  = "for Arrows1";     // Unique ID for arrows
input double              arrowsUpperGap    = 1.0;               // Upper arrow gap
input double              arrowsLowerGap    = 1.0;               // Lower arrow gap
input color               arrowsUpColor     = clrBlue;           // Up arrow color
input color               arrowsDnColor     = clrCrimson;        // Down arrow color
input int                 arrowsUpCode      = 221;               // Up arrow code
input int                 arrowsDnCode      = 222;               // Down arrow code
input int                 arrowsUpSize      = 2;                 // Up arrow size
input int                 arrowsDnSize      = 2;                 // Down arrow size
input bool                Interpolate       = true;              // Interpolate in multi time frame mode
  
double force[],upH[],dnH[],sig[],valc[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,Force_Period,Force_Method,Force_Price,SigLen,SigMeth,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsNotify,alertsEmail,soundFile,arrowsVisible,arrowsOnNewest,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

int OnInit()
{
   IndicatorBuffers(6);
   SetIndexBuffer(0,force,INDICATOR_DATA);  
   SetIndexBuffer(1,upH,  INDICATOR_DATA); 
   SetIndexBuffer(2,dnH,  INDICATOR_DATA);
   SetIndexBuffer(3,sig,  INDICATOR_DATA);   
   SetIndexBuffer(4,valc);                
   SetIndexBuffer(5,count);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period); 
   
   IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(TimeFrame)+ " Force ("+(string)Force_Period+")");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{ 
    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);
    }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

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=rates_total-prev_calculated+1; if (i>=rates_total) i=rates_total-1; count[0]=i;
      if (TimeFrame!=_Period)
      {
         i = (int)fmax(i,fmin(rates_total-1,_mtfCall(5,0)*TimeFrame/_Period));
         for (; i>=0 && !_StopFlag; i--)
         {
             int y = iBarShift(NULL,TimeFrame,time[i]);
                force[i] = _mtfCall(0,y);
                upH[i]   = _mtfCall(1,y);
                dnH[i]   = _mtfCall(2,y);
                sig[i]   = _mtfCall(3,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 btime = iTime(NULL,TimeFrame,y);
                     for(n = 1; (i+n)<rates_total && time[i+n] >= btime; n++) continue;	
                     for(k = 1; k<n && (i+n)<rates_total && (i+k)<rates_total; k++) 
                     {
                        _interpolate(force); 
                        _interpolate(sig); 
                        if (upH[i]!= EMPTY_VALUE) upH[i+k] = force[i+k];
  	                     if (dnH[i]!= EMPTY_VALUE) dnH[i+k] = force[i+k];
                     }                                                                    
              }  
   return(rates_total);
   }
   
   //
   //
   //
   //
   //
   
   for (; i>=0 && !_StopFlag; i--)
   {
       force[i] = iForce(NULL,0,Force_Period,Force_Method,Force_Price,i); 
       sig[i]   = iCustomMa(SigMeth,force[i],SigLen,i,rates_total);   
       valc[i]  = (i<rates_total-1) ? (force[i]>=0) ? 1 : (force[i]<0) ? -1 : valc[i+1] : 0 ;
       upH[i]   = (valc[i]== 1) ? force[i] : EMPTY_VALUE;
       dnH[i]   = (valc[i]==-1) ? force[i] : EMPTY_VALUE;
       
       //
       //
       //
       //
       //
      
       if (arrowsVisible)
       {
         string lookFor = arrowsIdentifier+":"+(string)Time[i]; ObjectDelete(lookFor);            
         if (i<(rates_total-1) && valc[i] != valc[i+1])
         {
            if (valc[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
            if (valc[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDnCode,arrowsDnSize, true);
         }
       }  
   }
   if (alertsOn)
   {
     int whichBar = (alertsOnCurrent) ? 0 : 1;
     if (valc[whichBar] != valc[whichBar+1])
     if (valc[whichBar] == 1)
           doAlert("crossing zero up");
     else  doAlert("crossing zero down");       
   }     
return(rates_total);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

#define _maInstances 1
#define _maWorkBufferx1 1*_maInstances
#define _maWorkBufferx2 2*_maInstances
#define _maWorkBufferx3 3*_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));
      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/(1.0+period))*(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);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

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)+" Force "+doWhat;
             if (alertsMessage) Alert(message);
             if (alertsNotify)  SendNotification(message);
             if (alertsEmail)   SendMail(_Symbol+" Force ",message);
             if (alertsSound)   PlaySound(soundFile);
      }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void drawArrow(int i,color theColor,int theCode, int theSize, bool up)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //
      //
      //

      datetime atime = Time[i]; if (arrowsOnNewest) atime += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,atime,0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         ObjectSet(name,OBJPROP_WIDTH,theSize);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}

//
//
//
//
//

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("");
}


