//------------------------------------------------------------------
#property  copyright "mladen"
#property  link      "mladenfx@gmail.com"
//------------------------------------------------------------------

#property  indicator_separate_window
#property  indicator_buffers   5
#property  indicator_color1    DarkGray
#property  indicator_color2    Orange
#property  indicator_color3    Orange
#property  indicator_color4    LimeGreen
#property  indicator_color5    LimeGreen
#property  indicator_width2    2
#property  indicator_width3    2
#property  indicator_width4    2
#property  indicator_width5    2
#property  indicator_maximum   1
#property  indicator_minimum  -1

//
//
//
//
//

extern string TimeFrame      = "Current time frame";
extern int    FishPeriod     =  32;
extern int    RSIPeriod      =  32;
extern int    RSIPrice       =  PRICE_CLOSE;
extern double OmaSpeed       = 1.5;
extern bool   OmaAdaptive    = true;
extern double SmoothPeriod   =  3;
extern double SmoothSpeed    =  3;
extern bool   SmoothAdaptive =  true;
extern double Level1         =  0.5;
extern double Level2         =  0.0;
extern double Level3         = -0.5;
extern bool   Interpolate    = true;

//
//
//
//
//

double ifishua[];
double ifishub[];
double ifishda[];
double ifishdb[];
double ifisher[];
double rsi[];
double avg[];
double trend[];

//
//
//
//
//

string indicatorFileName;
bool   returnBars;
bool   calculateValue;
int    timeFrame;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(8);
      SetIndexBuffer(0,ifisher);
      SetIndexBuffer(1,ifishda);
      SetIndexBuffer(2,ifishdb);
      SetIndexBuffer(3,ifishua);
      SetIndexBuffer(4,ifishub);
      SetIndexBuffer(5,rsi);
      SetIndexBuffer(6,avg);
      SetIndexBuffer(7,trend);

      //
      //
      //
      //
      //
      
      indicatorFileName = WindowExpertName();
      returnBars        = TimeFrame=="returnBars";     if (returnBars)     return(0);
      calculateValue    = TimeFrame=="calculateValue";
      
         SetLevelValue(0,Level1);
         SetLevelValue(1,Level2);
         SetLevelValue(2,Level3);

   //
   //
   //
   //
   //
            
   timeFrame = stringToTimeFrame(TimeFrame);
   IndicatorShortName(timeFrameToString(timeFrame)+" Fisher transform of OMA RSI ("+RSIPeriod+","+DoubleToStr(SmoothPeriod,2)+")");
   return(0);
}
int deinit()
{
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int start()
{
   int    limit, i; 
   int    counted_bars=IndicatorCounted();
   
   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
           limit=MathMax(Bars-counted_bars,Bars-1);
           if (returnBars) { ifisher[0] = limit+1; return(0); }

   //
   //
   //
   //
   //
   
   if (calculateValue || timeFrame==Period())
   {
      if (!calculateValue) if (trend[limit]== 1) CleanPoint(limit,ifishua,ifishub);
      if (!calculateValue) if (trend[limit]==-1) CleanPoint(limit,ifishda,ifishdb);

      //
      //
      //
      //
      //
      
      for(i=limit; i>=0; i--) 
      for(i=limit; i>=0; i--)
      {
         rsi[i]     = 0.1*(iRsi(iMA(NULL,0,1,0,MODE_SMA,RSIPrice,i),RSIPeriod,OmaSpeed,OmaAdaptive,i)-50);      
         avg[i]     = iOma(rsi[i],SmoothPeriod,SmoothSpeed,SmoothAdaptive,i,2);
         double MaxH = avg[ArrayMaximum(avg,FishPeriod,i)];
         double MinL = avg[ArrayMinimum(avg,FishPeriod,i)];
         if (MaxH!=MinL)
               ifisher[i] = 0.33*2.0*((avg[i]-MinL)/(MaxH-MinL)-0.5)+0.67*ifisher[i+1];
         else  ifisher[i] = 0.00;
               ifisher[i] = MathMin(MathMax(ifisher[i],-1),1); 
         trend[i]   = trend[i+1];
            if (ifisher[i] < Level1 && ifisher[i] > Level3) trend[i] =  0;
            if (ifisher[i] > Level1)                        trend[i] =  1;
            if (ifisher[i] < Level3)                        trend[i] = -1;
            if (calculateValue) continue;
            
         //
         //
         //
         //
         //

            ifishda[i] = EMPTY_VALUE; ifishdb[i] = EMPTY_VALUE;         
            ifishua[i] = EMPTY_VALUE; ifishub[i] = EMPTY_VALUE;         
               if (trend[i]== 1) PlotPoint(i,ifishua,ifishub,ifisher);
               if (trend[i]==-1) PlotPoint(i,ifishda,ifishdb,ifisher);
      }
      return(0);
   }      

   //
   //
   //
   //
   //

   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period()));

   if (trend[limit]== 1) CleanPoint(limit,ifishua,ifishub);
   if (trend[limit]==-1) CleanPoint(limit,ifishda,ifishdb);
   for(i=limit; i>=0; i--)
   {
      int y = iBarShift(NULL,timeFrame,Time[i]);
         trend[i]   = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",FishPeriod,RSIPeriod,RSIPrice,OmaSpeed,OmaAdaptive,SmoothPeriod,SmoothSpeed,SmoothAdaptive,Level1,Level2,Level3,7,y);
         ifisher[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue",FishPeriod,RSIPeriod,RSIPrice,OmaSpeed,OmaAdaptive,SmoothPeriod,SmoothSpeed,SmoothAdaptive,Level1,Level2,Level3,0,y);
         ifishda[i] = EMPTY_VALUE; 
         ifishdb[i] = EMPTY_VALUE;         
         ifishua[i] = EMPTY_VALUE; 
         ifishub[i] = EMPTY_VALUE;         

         //
         //
         //
         //
         //
      
         if (!Interpolate || y==iBarShift(NULL,timeFrame,Time[i-1])) continue;

         //
         //
         //
         //
         //

         datetime time = iTime(NULL,timeFrame,y);
            for(int n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;	
            double factor = 1.0 / n;
            for(int k = 1; k < n; k++)
               ifisher[i+k] = k*factor*ifisher[i+n] + (1.0-k*factor)*ifisher[i];
   }
   for(i=limit; i>=0; i--)
   {
      if (trend[i]== 1) PlotPoint(i,ifishua,ifishub,ifisher);
      if (trend[i]==-1) PlotPoint(i,ifishda,ifishdb,ifisher);
   }
   return(0);
}


//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
//

double workRsi[][3];
#define _price  0
#define _change 1
#define _changa 2

double iRsi(double price, double period, double speed, bool adaptive, int i, int instanceNo=0)
{
   if (ArrayRange(workRsi,0)!=Bars) ArrayResize(workRsi,Bars);
      int z = instanceNo*3; 
      int r = Bars-i-1;
   
   //
   //
   //
   //
   //
   
   workRsi[r][z+_price] = price;
         double chng   = workRsi[r][_price]-workRsi[r-1][_price];
         double changn = iOma(        chng ,period,speed,adaptive,i,instanceNo*2+0);
         double changa = iOma(MathAbs(chng),period,speed,adaptive,i,instanceNo*2+1);
            if (changn != 0)
                  return(MathMin(MathMax(50.0*(changn/MathMax(changa,0.0000001)+1.0),0),100));
            else  return(50.0);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

double workOma[][21];
#define F01 0
#define F02 1
#define F03 2
#define F04 3
#define F05 4
#define F06 5
#define prc 6

//
//
//
//
//

double iOma(double price, double averagePeriod, double constant, bool adaptive, int r, int s=0)
{
   if (averagePeriod <=1) return(price);
   if (ArrayRange(workOma,0) != Bars) ArrayResize(workOma,Bars); r=Bars-r-1; s *=7;
   if (r<=1) 
   {
      for (int i=0; i<6; i++) workOma[r][i  +s] = 0;
                              workOma[r][prc+s] = price;
                              return(price);
   }      
   double f01=workOma[r-1][F01+s];  double f02=workOma[r-1][F02+s];
   double f03=workOma[r-1][F03+s];  double f04=workOma[r-1][F04+s];
   double f05=workOma[r-1][F05+s];  double f06=workOma[r-1][F06+s];

   //
   //
   //
   //
   //

      if (adaptive && (averagePeriod > 1))
      {
         double minPeriod = MathMin(averagePeriod,r)/2.0;
         double maxPeriod = MathMin(minPeriod*5.0,r);
         int    endPeriod = (int)MathCeil(maxPeriod);
         double signal    = MathAbs((price-workOma[r-endPeriod][prc+s]));
         double noise     = 0.00000000001;

            for(i=1; i<endPeriod; i++) noise=noise+MathAbs(price-workOma[r-i][prc+s]);

         averagePeriod = ((signal/noise)*(maxPeriod-minPeriod))+minPeriod;
      }
      
      //
      //
      //
      //
      //
      
      double Kg = (2.0+constant)/(1.0+constant+averagePeriod);
      double Hg = 1.0-Kg;

      f01 = Kg * price + Hg * f01; f02 = Kg * f01 + Hg * f02; double v01 = 1.5 * f01 - 0.5 * f02;
      f03 = Kg * v01   + Hg * f03; f04 = Kg * f03 + Hg * f04; double v02 = 1.5 * f03 - 0.5 * f04;
      f05 = Kg * v02   + Hg * f05; f06 = Kg * f05 + Hg * f06; double v03 = 1.5 * f05 - 0.5 * f06;

   //
   //
   //
   //
   //

   workOma[r][F01+s] = f01;  workOma[r][F02+s] = f02;
   workOma[r][F03+s] = f03;  workOma[r][F04+s] = f04;
   workOma[r][F05+s] = f05;  workOma[r][F06+s] = f06;
   workOma[r][prc+s] = price;
   return(v03);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   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 (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};

//
//
//
//
//

int stringToTimeFrame(string tfs)
{
   tfs = StringUpperCase(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+iTfTable[i]) return(MathMax(iTfTable[i],Period()));
                                                      return(Period());
}
string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

string StringUpperCase(string str)
{
   string   s = str;

   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int tchar = StringGetChar(s, length);
         if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
                     s = StringSetChar(s, length, tchar - 32);
         else if(tchar > -33 && tchar < 0)
                     s = StringSetChar(s, length, tchar + 224);
   }
   return(s);
}