//+------------------------------------------------------------------+
//|                                       Fractal dimesion index.mq4 |
//|                                                           mladen |
//|                                                                  |
//| Modification of original FDI made after jppoton version          |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "www.forex-station.com"

#property indicator_separate_window
#property indicator_buffers 7
#property indicator_color1  clrDeepSkyBlue
#property indicator_color2  clrPaleVioletRed
#property indicator_color3  clrDeepSkyBlue
#property indicator_color4  clrPaleVioletRed
#property indicator_color5  clrPaleVioletRed
#property indicator_color6  clrDimGray
#property indicator_color7  clrDimGray
#property indicator_width3  2
#property indicator_width4  2
#property indicator_width5  2
#property strict

//
//
//

input int                  FDILength          = 30;           // Fdi length
input int                  inpSmthPer         = 5;            // Price smoothing period
input ENUM_MA_METHOD       inpSmthMode        = MODE_EMA;     // Price smoothing period
input ENUM_APPLIED_PRICE   inpPrice           = PRICE_CLOSE;  // Fdi price
input double               inpFdiThresh       = 1.5;          // Fdi threshold
input string               __alr__00          = "";           //.Alerts settings
input bool                 alertsOn           = true;         // Alerts?
input bool                 alertsOnEveryAlign = true;         // Alerts every alignment?
input bool                 alertsOnCurrent    = false;        // Alerts open bar?
input bool                 alertsOnAlignUp    = true;         // Alerts up alignment?
input bool                 alertsOnAlignDn    = true;         // Alerts down alignment?
input bool                 alertsMessage      = true;         // Alerts message?
input bool                 alertsSound        = false;        // Alerts sound?
input bool                 alertsEmail        = false;        // Alerts email?
input bool                 alertsPush         = false;        // Alerts notification?

double fdi[],fdia[],fdib[],bup[],bdn[],huu[],hdd[],prc[];
struct sGlobalStruct
{
   double   dx2,dy,max,min,pdi,len,std;
   int      lim,wBar; 
};
sGlobalStruct glo;

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------

int OnInit()
{
   IndicatorBuffers(8);
   SetIndexBuffer(0,huu, INDICATOR_DATA); SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexBuffer(1,hdd, INDICATOR_DATA); SetIndexStyle(1,DRAW_HISTOGRAM);
   SetIndexBuffer(2,fdi, INDICATOR_DATA); SetIndexStyle(2,DRAW_LINE);
   SetIndexBuffer(3,fdia,INDICATOR_DATA); SetIndexStyle(3,DRAW_LINE);
   SetIndexBuffer(4,fdib,INDICATOR_DATA); SetIndexStyle(4,DRAW_LINE);
   SetIndexBuffer(5,bup, INDICATOR_DATA); SetIndexStyle(5,DRAW_LINE);
   SetIndexBuffer(6,bdn, INDICATOR_DATA); SetIndexStyle(6,DRAW_LINE); 
   SetIndexBuffer(7,prc, INDICATOR_CALCULATIONS);
   
   glo.dx2 = pow(1.0/FDILength,2);
   
   IndicatorSetInteger(INDICATOR_LEVELS,1);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0);
   
   IndicatorSetString(INDICATOR_SHORTNAME,"Fractal dimension index (jppoton) ("+(string)FDILength+","+(string)inpFdiThresh+")");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { }

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------

double trends[][2];
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[])
{
   glo.lim = fmin(rates_total-prev_calculated+1,rates_total-1);
   if (ArrayRange(trends,0)!= rates_total) ArrayResize(trends,rates_total);
      
   //
   //
   //
 
   if (trends[rates_total-glo.lim-1][0]==-1) iCleanPoint(glo.lim,rates_total,fdia,fdib);
   for(int i=glo.lim, r=rates_total-glo.lim-1; i>=0; i--,r++)
   {
      prc[i]  = iMA(_Symbol,_Period,1,0,MODE_SMA,inpPrice,i);
      glo.max = prc[ArrayMaximum(prc,FDILength,i)];
      glo.min = prc[ArrayMinimum(prc,FDILength,i)];
      glo.len = 0.00;
      if (glo.max == glo.min) glo.len = 1;
      else         
      {
         glo.pdi = glo.max-glo.min;
         for (int k=0; k<FDILength && (i+k+1)<rates_total; k++)
         {
             glo.dy   = (prc[i+k+1]-prc[i+k])/glo.pdi;
             glo.len += sqrt(glo.dx2+glo.dy*glo.dy);
         }
       }
       fdi[i]       = 1.0+(log(glo.len)+log(2))/log(2.0*FDILength)-inpFdiThresh;
       trends[r][0] = (r>0) ? (fdi[i]>0) ? 1 : (fdi[i]<0) ? -1 : trends[r-1][0] : 0;   
       if (trends[r][0]==-1) iPlotPoint(i,rates_total,fdia,fdib,fdi); else fdia[i] = fdib[i] = EMPTY_VALUE;
   }         
   for(int i=glo.lim, r=rates_total-glo.lim-1; i>=0; i--,r++)
   {
      huu[i]  = hdd[i] = EMPTY_VALUE;
      glo.std = iStdDevOnArray(fdi,0,FDILength,0,MODE_SMA,i);
      bup[i]  = fdi[i]+glo.std;
      bdn[i]  = fdi[i]-glo.std;
      huu[i]  = fmax(bdn[i],0);
      hdd[i]  = fmin(bup[i],0);
      if (r>0)
      {
         trends[r][1] = (alertsOnEveryAlign) ? 0 : trends[r-1][1]; 
         if (fdi[i]>0) trends[r][1] =  1;
         if (fdi[i]<0) trends[r][1] = -1;
      }
   }
   if (alertsOn)
   {
      glo.wBar = (alertsOnCurrent) ? rates_total-1 : rates_total-2;
      if (trends[glo.wBar][1] != trends[glo.wBar-1][1])
      {
         if (alertsOnAlignUp && trends[glo.wBar][1] == 1) doAlert(" up");
         if (alertsOnAlignDn && trends[glo.wBar][1] ==-1) doAlert(" down");
      }
   }
return(rates_total);
}

//
//
//

void doAlert(string doWhat, int instanceNo=0, int forBar=-1)
{
   class sWorkStruct
   {
      public :
         datetime prevTime;
         string   prevMessage;
         
         void sWorkStruct() : prevTime(-1), prevMessage("") {};
   };
   static sWorkStruct m_array[];
   static int         m_arraySize = -1;
                  if (m_arraySize<instanceNo+1) m_arraySize = ArrayResize(m_array,instanceNo+1);
   
   //
   //
   //
   
   if (m_array[instanceNo].prevMessage != doWhat || m_array[instanceNo].prevTime != iTime(_Symbol,_Period,0)) 
   {
      m_array[instanceNo].prevMessage  = doWhat;
      m_array[instanceNo].prevTime     = iTime(_Symbol,_Period,0);

      //
      //
      //

      #ifndef _timeFrameToString
         #define _timeFrameToString(_tf) StringSubstr(EnumToString((ENUM_TIMEFRAMES)_tf),7)
      #endif
      
      //
      //
      //
      
      string message = _timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToString(TimeLocal(),TIME_SECONDS)+" FDI all values aligned "+doWhat;
         if (alertsMessage) Alert(message);
         if (alertsEmail)   SendMail(" FDI all values aligned",message);
         if (alertsPush)    SendNotification(message);
         if (alertsSound)   PlaySound("alert2.wav");
   }
}


//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------

void iCleanPoint(int i, int bars,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 iPlotPoint(int i, int bars,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; }
}