/*------------------------------------------------------------------------------------
   Name: Stoch-Tape.mq4
   Copyright ｩ2012, Xaphod, http://www.xaphod.com
   
   Description: MTF Stochastic Tape Chart Indicator.
                Features:
                - Divider line to group sets of lower timeframe bars to ease reading 
                  the chart.
                - Different colors for rising/overbaught and falling/oversold.  

   Change log: 
       2012-09-13. Xaphod, v1.01
          - Remove gaps on 4 digit brokers caused by lack of resolution
       2012-07-16. Xaphod, v1.00 
          - First Release 
-------------------------------------------------------------------------------------*/
// Indicator properties
#property copyright "Copyright ｩ2012, xaphod.com"
#property link      "http://www.xaphod.com"

#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1 RoyalBlue
#property indicator_color2 Crimson
#property indicator_color3 Silver
#property indicator_width1  4
#property indicator_width2  4
#property indicator_width3  1
#property indicator_maximum 1
#property indicator_minimum 0

//#include <xPrint.mqh>

// Constant definitions
#define INDICATOR_NAME "Stoch"
#define INDICATOR_VERSION "1.01"
#define DIVIDER_LINE 100000

// indicator parameters
extern string    Indi.Version= INDICATOR_VERSION;
extern string    TimeFrame.Settings="覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧";
extern int       TimeFrame.Period=0;            // Timeframe: 0,1,5,15,30,60,240,1440 etc. Current Timeframe=0. 
extern int       TimeFrame.Auto=1;              // Automatically select higher TF. M15 and M30 -> H1. Off=0, 1st HTF=1, 2nd HTF=2
extern string    Stoch.Settings="覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧";
extern int       Stoch.KPeriod=11;
extern int       Stoch.Slowing=3;
extern int       Stoch.MAMethod=MODE_SMA;       // "<< SMA=0, EMA=1, SMMA=2, LWMA=3 >>";
extern int       Stoch.PriceField=1;            // "<< 0=High/Low, 1=Close/Close >>";
extern int       Stoch.Low=20;
extern int       Stoch.High=80;

// indicator buffers
double gadUp[];
double gadDn[];
double gadLine[];

// Globals
int giRepaintBars;
bool gbInit;


//+------------------------------------------------------------------+
int init() {
  IndicatorBuffers(3); 
  SetIndexStyle(0,DRAW_HISTOGRAM);
  SetIndexBuffer(0,gadUp);
  SetIndexLabel(0,NULL);
  SetIndexStyle(1,DRAW_HISTOGRAM);
  SetIndexBuffer(1,gadDn);
  SetIndexLabel(1,NULL);
  SetIndexStyle(2,DRAW_LINE);
  SetIndexBuffer(2,gadLine);
  SetIndexLabel(2,NULL);
  
  // Set Timeframe
  switch(TimeFrame.Auto) {
    case 1: 
      TimeFrame.Period=NextHigherTF(TimeFrame.Period); 
      giRepaintBars=TimeFrame.Period/Period()+2;
    break;
    case 2: 
      TimeFrame.Period=NextHigherTF(NextHigherTF(TimeFrame.Period));
      giRepaintBars=TimeFrame.Period/Period()+2;
    break;
    default: 
      if (TimeFrame.Period<1 || TimeFrame.Period==Period()) {
        TimeFrame.Period=Period();
        giRepaintBars=0;
      }
      else {
        giRepaintBars=TimeFrame.Period/Period()+2;
      }
    break;
  }
  
  IndicatorShortName(TF2Str(TimeFrame.Period)+"-Stoch("+Stoch.KPeriod+","+Stoch.Slowing+") ");
  gbInit=True;
  return(0);
}

//+------------------------------------------------------------------+
int deinit() {
   return (0);
}


//+------------------------------------------------------------------+
int start() {
  int i, j, iNewBars, iCountedBars;
  static int iDivider=-1;
  static int iHTFBar=-1;
  double dSig0, dSig1;
  int iPeriodSec=TimeFrame.Period*60;  
  static datetime tNextBar;
  
  if (gbInit) {
    tNextBar=Time[Bars-1]/iPeriodSec;
    tNextBar=tNextBar*iPeriodSec+iPeriodSec;
    gbInit=False;
  }
  
  // Get unprocessed bars
  iCountedBars=IndicatorCounted();
  if(iCountedBars < 0) return (-1); 
  if(iCountedBars>0) iCountedBars--;
  
  // Set bars to redraw
  if (NewBars(TimeFrame.Period)>3)
    iNewBars=Bars-1;
  else
    iNewBars=Bars-iCountedBars;
  if (iNewBars<giRepaintBars)
    iNewBars=giRepaintBars;
  
  for(i=iNewBars; i>=0; i--) {
    // Shift index for higher time-frame
    if (TimeFrame.Period>Period() )
      j=iBarShift(Symbol(), TimeFrame.Period, Time[i]);
    else
      j=i;
    // Calc Stoch
    if (iHTFBar!=j) {
      iHTFBar=j;
      dSig0=iStochastic(Symbol(),TimeFrame.Period,Stoch.KPeriod,1,Stoch.Slowing,Stoch.MAMethod,Stoch.PriceField,MODE_MAIN,j);
      dSig1=iStochastic(Symbol(),TimeFrame.Period,Stoch.KPeriod,1,Stoch.Slowing,Stoch.MAMethod,Stoch.PriceField,MODE_MAIN,j+1);
      if (gadLine[i]==EMPTY_VALUE) {
        iDivider *=-1;
      }  
    }
    
    if (gadLine[i]==EMPTY_VALUE)
        gadLine[i]=iDivider*DIVIDER_LINE;
        
    if (iClose(NULL,TimeFrame.Period,j)==0) {
      gadUp[i]=EMPTY_VALUE;
      gadDn[i]=EMPTY_VALUE;
    }  
    else if (dSig0>=dSig1 && dSig0>Stoch.Low) {
      gadUp[i]=1;
      gadDn[i]=EMPTY_VALUE;
    }
    else if (dSig0<dSig1 && dSig0<Stoch.High) {
      gadUp[i]=EMPTY_VALUE;
      gadDn[i]=1;
    }  
    else if (dSig0>Stoch.High) {
      gadUp[i]=1;
      gadDn[i]=EMPTY_VALUE;
    }
    else if (dSig0<Stoch.Low) {
      gadUp[i]=EMPTY_VALUE;
      gadDn[i]=1;
    }
  }
  
  return(0);
}
//+------------------------------------------------------------------+


//-----------------------------------------------------------------------------
// function: NewBars()
// Description: Return nr of new bars on a TF
//-----------------------------------------------------------------------------
int NewBars(int iPeriod) {
  static int iPrevSize=0;
  int iNewSize;
  int iNewBars;
  datetime tTimeArray[];
  
  ArrayCopySeries(tTimeArray,MODE_TIME,Symbol(),iPeriod);
  iNewSize=ArraySize(tTimeArray);
  iNewBars=iNewSize-iPrevSize;
  iPrevSize=iNewSize;
  return(iNewBars);
}


//-----------------------------------------------------------------------------
// function: TF2Str()
// Description: Convert time-frame to a string
//-----------------------------------------------------------------------------
string TF2Str(int iPeriod) {
  switch(iPeriod) {
    case PERIOD_M1: return("M1");
    case PERIOD_M5: return("M5");
    case PERIOD_M15: return("M15");
    case PERIOD_M30: return("M30");
    case PERIOD_H1: return("H1");
    case PERIOD_H4: return("H4");
    case PERIOD_D1: return("D1");
    case PERIOD_W1: return("W1");
    case PERIOD_MN1: return("MN1");
    default: return("M"+iPeriod);
  }
  return(0);
}


//-----------------------------------------------------------------------------
// function: NextHigherTF()
// Description: Select the next higher time-frame. 
//              Note: M15 and M30 both select H1 as next higher TF. 
//-----------------------------------------------------------------------------
int NextHigherTF(int iPeriod) {
  if (iPeriod==0) iPeriod=Period();
  switch(iPeriod) {
    case PERIOD_M1: return(PERIOD_M5);
    case PERIOD_M5: return(PERIOD_M15);
    case PERIOD_M15: return(PERIOD_H1);
    case PERIOD_M30: return(PERIOD_H1);
    case PERIOD_H1: return(PERIOD_H4);
    case PERIOD_H4: return(PERIOD_D1);
    case PERIOD_D1: return(PERIOD_W1);
    case PERIOD_W1: return(PERIOD_MN1);
    case PERIOD_MN1: return(PERIOD_MN1);
    default: return(Period());
  }
  return(Period());
}