//+------------------------------------------------------------------+
//|                                                        frama.mq4 |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers 6
#property indicator_color1  clrSilver
#property indicator_color2  clrSilver
#property indicator_color3  clrDeepSkyBlue
#property indicator_color4  clrDeepSkyBlue
#property indicator_color5  clrSandyBrown
#property indicator_color6  clrSandyBrown
#property strict

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame        = PERIOD_CURRENT; // Time frame to use
extern int                FramaPeriod      = 25;             // Frama period
extern double             FramaMultiplier  = 4.6;            // Frama multiplier
extern double             SignalMultiplier = 2.5;            // Signal multiplier
extern ENUM_APPLIED_PRICE FramaPrice       = PRICE_CLOSE;    // Price to use
extern bool               alertsOn         = false;          // Turn alerts on
extern bool               alertsOnCurrent  = false;          // Alerts on current (still opened) bar?
extern bool               alertsMessage    = true;           // Alerts should display a pop-up message?
extern bool               alertsSound      = true;           // Alerts should play an alert sound?
extern bool               alertsEmail      = false;          // Alerts should send an email?
extern bool               alertsNotify     = false;          // Alerts should send push notification?
extern int                LinesWidth       = 2;              // Lines display width
extern bool               Interpolate      = true;           // Interpolate in multi time frame

double frmBuffer[],frsBuffer[],frmBufferda[],frmBufferdb[],frmBufferua[],frmBufferub[],state[];
string indicatorFileName;
bool   returnBars;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(7);
   SetIndexBuffer(0,frmBuffer);
   SetIndexBuffer(1,frsBuffer);
   SetIndexBuffer(2,frmBufferua);
   SetIndexBuffer(3,frmBufferub);
   SetIndexBuffer(4,frmBufferda);
   SetIndexBuffer(5,frmBufferdb);
   SetIndexBuffer(6,state);
      for(int i=0; i<6; i++) SetIndexStyle(i,EMPTY,EMPTY,LinesWidth);
      indicatorFileName = WindowExpertName();
      returnBars        = TimeFrame==-99;
      TimeFrame         = MathMax(TimeFrame,_Period);
   IndicatorShortName("Frama ribbon ("+(string)FramaPeriod+")");         
   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) { frmBuffer[0] = limit+1; return(0); }
            if (TimeFrame != Period())
            {
               limit = (int)MathMax(limit,MathMin(Bars-1,iCustom(NULL,TimeFrame,indicatorFileName,-99,0,0)*TimeFrame/_Period));
               if (state[limit]== 1) CleanPoint(limit,frmBufferua,frmBufferub);
               if (state[limit]==-1) CleanPoint(limit,frmBufferda,frmBufferdb);
               for(int i = limit; i >= 0; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                  frmBuffer[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,FramaPeriod,FramaMultiplier,SignalMultiplier,FramaPrice,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsNotify,0,y); 
                  frsBuffer[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,FramaPeriod,FramaMultiplier,SignalMultiplier,FramaPrice,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsNotify,1,y); 
                  state[i]     = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,FramaPeriod,FramaMultiplier,SignalMultiplier,FramaPrice,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsNotify,6,y); 
                  frmBufferda[i] = EMPTY_VALUE;
                  frmBufferdb[i] = EMPTY_VALUE;
                  frmBufferua[i] = EMPTY_VALUE;
                  frmBufferub[i] = EMPTY_VALUE;
         
                  //
                  //
                  //
                  //
                  //
      
                  if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
                  
                  //
                  //
                  //
                  //
                  //
                  
                  int n,j; datetime time = iTime(NULL,TimeFrame,y);
                     for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;	
                     for(j = 1; j<n && (i+n)<Bars && (i+j)<Bars; j++)
                     {
                        frmBuffer[i+j] = frmBuffer[i] + (frmBuffer[i+n] - frmBuffer[i])*j/n;
                        frsBuffer[i+j] = frsBuffer[i] + (frsBuffer[i+n] - frsBuffer[i])*j/n;
                     }
               }
               for(int i = limit; i >= 0; i--)
               {
                  if (state[i] ==  1) PlotPoint(i,frmBufferua,frmBufferub,frmBuffer);
                  if (state[i] == -1) PlotPoint(i,frmBufferda,frmBufferdb,frmBuffer);
               }
               return(0); 
            }

   //
   //
   //
   //
   //

      if (state[limit]== 1) CleanPoint(limit,frmBufferua,frmBufferub);
      if (state[limit]==-1) CleanPoint(limit,frmBufferda,frmBufferdb);
      for(int i = limit; i >= 0; i--)
      {
         frmBuffer[i]   = iFrama(iMA(NULL,0,1,0,MODE_SMA,FramaPrice,i),FramaPeriod,FramaMultiplier ,i,0);
         frsBuffer[i]   = iFrama(frmBuffer[i]                         ,FramaPeriod,SignalMultiplier,i,1);
         frmBufferda[i] = EMPTY_VALUE;
         frmBufferdb[i] = EMPTY_VALUE;
         frmBufferua[i] = EMPTY_VALUE;
         frmBufferub[i] = EMPTY_VALUE;
            if (i<Bars-1) state[i] = state[i+1];
            if (frmBuffer[i]>frsBuffer[i]) state[i] =  1;
            if (frmBuffer[i]<frsBuffer[i]) state[i] = -1;
            if (state[i] ==  1) PlotPoint(i,frmBufferua,frmBufferub,frmBuffer);
            if (state[i] == -1) PlotPoint(i,frmBufferda,frmBufferdb,frmBuffer);
      }         
   manageAlerts();
   return(0);      
}


//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0;
      if (state[whichBar] != state[whichBar+1])
      {
         if (state[whichBar]== 1) doAlert(whichBar,"trend changed to up");
         if (state[whichBar]==-1) doAlert(whichBar,"trend changed to down");
      }
   }
}

//
//
//
//
//

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 = timeFrameToString(_Period)+" "+Symbol()+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" frama ribbon "+doWhat;
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(Symbol()+" frama ribbon ",message);
          if (alertsNotify)  SendNotification(message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

#define framaInstances 2
double workFrama[][framaInstances];
double iFrama(double price, int fperiod, double fmultiplier, int i, int instanceNo=0)
{
   if (ArrayRange(workFrama,0)!=Bars) ArrayResize(workFrama,Bars); int r = Bars-i-1;
   
   //
   //
   //
   //
   //
   
   if (r<1)
      workFrama[r][instanceNo] = price;
   else
   {      
      double halfPeriod = fperiod/2;
      double hh = High[iHighest(NULL,0,MODE_HIGH,fperiod,i)];
      double ll = Low [iLowest (NULL,0,MODE_LOW ,fperiod,i)];
      double n3 = (hh-ll)/FramaPeriod;
             hh = High[iHighest(NULL,0,MODE_HIGH,(int)halfPeriod,i)];
             ll = Low [iLowest (NULL,0,MODE_LOW ,(int)halfPeriod,i)];
      double n1 = (hh-ll)/halfPeriod;
      int   ind = MathMin(i+(int)halfPeriod,Bars-1);
             hh = High[iHighest(NULL,0,MODE_HIGH,(int)halfPeriod,ind)];
             ll = Low [iLowest (NULL,0,MODE_LOW ,(int)halfPeriod,ind)];
      double n2 = (hh-ll)/halfPeriod;
      double dimen = 0;
            if (n1>0 && n2>0 && n3>0) dimen = (MathLog(n1+n2)-MathLog(n3))/MathLog(2.0);
      double alpha = MathMin(MathMax(MathExp(-fmultiplier*(dimen-1.0)),0.01),1.0);
      workFrama[r][instanceNo] = alpha*price+(1-alpha)*workFrama[r-1][instanceNo];
   }
   return(workFrama[r][instanceNo]);      
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

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("");
}