//------------------------------------------------------------------
#property copyright "mladen"
#property link      "mladenfx@gmail.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1  clrYellow
#property indicator_color2  clrBlue
#property strict

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame    = PERIOD_CURRENT;  // Time frame
extern int                TSIPeriod1   = 7;               // TSI period 1
extern int                TSIPeriod2   = 4;               // TSI period 2
extern int                SignalPeriod = 3;               // Signal period
extern ENUM_APPLIED_PRICE Price        = PRICE_TYPICAL;   // Price
extern bool               Interpolate  = true;            // Interpolate in multi time frame mode?

double TSIBuffer[],SignalBuffer[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,TSIPeriod1,TSIPeriod2,SignalPeriod,Price,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int OnInit()
{
   IndicatorBuffers(3);
   SetIndexBuffer(0,TSIBuffer);    SetIndexStyle(0,DRAW_LINE); SetIndexLabel(0,"TSI");
   SetIndexBuffer(1,SignalBuffer); SetIndexStyle(1,DRAW_LINE); SetIndexLabel(1,"TSI Signal");
   SetIndexBuffer(2,count);
   
      //
      //
      //
      //
      //
      
         indicatorFileName = WindowExpertName();
         TimeFrame         = MathMax(TimeFrame,_Period);
      
      //
      //
      //
      //
      //

   IndicatorShortName(timeFrameToString(TimeFrame)+" TSI ("+(string)TSIPeriod1+","+(string)TSIPeriod2+","+(string)SignalPeriod+")");
   return(INIT_SUCCEEDED);
}

//
//
//
//
//

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(2,0)*TimeFrame/_Period));
            for (int i=limit; i>=0; i--)
            {
               int y = iBarShift(NULL,TimeFrame,Time[i]);
                  TSIBuffer[i]    = _mtfCall(0,y);
                  SignalBuffer[i] = _mtfCall(1,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 time = iTime(NULL,TimeFrame,y);
                        for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;	
                        for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++) 
                        {
                           _interpolate(TSIBuffer);
                           _interpolate(SignalBuffer);
                        }                  
            }
            return(0);
         }

   //
   //
   //
   //
   //
   
      for (int i=limit; i>=0; i--)
      {
         double priceDiff = iMA(NULL,0,1,0,MODE_SMA,Price,i)-iMA(NULL,0,1,0,MODE_SMA,Price,i+1);
         double avg = iEma(iEma(        priceDiff ,TSIPeriod1,i,0),TSIPeriod2,i,1);
         double ava = iEma(iEma(MathAbs(priceDiff),TSIPeriod1,i,2),TSIPeriod2,i,3);
            if (ava != 0)
                  TSIBuffer[i] = 100.0*avg/ava;
            else  TSIBuffer[i] = 0.00;
            SignalBuffer[i] = iEma(TSIBuffer[i],SignalPeriod,i,4);
      }
      return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workEma[][5];
double iEma(double price, double period, int r, int instanceNo=0)
{
   if (ArrayRange(workEma,0)!= Bars) ArrayResize(workEma,Bars); r = Bars-r-1;

   //
   //
   //
   //
   //
      
   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]);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

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("");
}