/*-------------------------------------------------------------------
   Name: Xi-AsianSession.mq4
   Copyright ©2010, Xaphod, http://forexwhiz.appspot.com
   
   Description: 
     Draws the Asian session channel on the chart.
     Optional alert when the channel range goes above a set threshold.
     Optional range value displayed under upper channel line.
     Optional vertical lines at beginning and end of channel.
     Known quirks: Session.OpenHour and Session.CloseHour must be at least 2 hours apart
   History:
   2010-07-22, Xaphod, v0.94
     Changed name to Xi-Sessions
     Added IndicatorId to allow multiple instances of the indicator on a chart
     BugFix. Starting bar was off one period when Session.CloseOnHour=true
     Refactoring
   2010-06-10, Xaphod, v0.93
     Added Session high/low lines  
   2010-04-21, Xaphod, v0.92
     Added Session.CloseOnHour to select which bar to draw close on
   2010-04-20, Xaphod, v0.91
     Fixed CloseVLine.Style type was wrong
   2010-04-16 - Xaphod, v0.90
     Initial Release
-------------------------------------------------------------------*/
#property copyright "Copyright © 2010, Xaphod"
#property link      "http://forexwhiz.appspot.com"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 PaleGoldenrod
#property indicator_color2 PaleGoldenrod
#property indicator_style1 3
#property indicator_style2 3

// Constants
#define INDICATOR_NAME "Xi-Sessions"
#define INDICATOR_VERSION "v0.94"

// Indicator parameters
extern string    Session.Info="<< Session >>";
extern string    Session.IndicatorId="Asian";     // Id of this indicator instance on the chart (String!)
extern int       Session.OpenHour=23;          // Session opening hour. 
extern int       Session.CloseHour=05;         // Session closing hour. NOTE: Session closes 1 min before this hour.
extern bool      Session.CloseOnHour=true;     // True: Channel and vline on the first bar of the hour. False: On the previous bar.
extern int       Session.NrOfDays=5;           // Nr of past days to draw the session channel for
extern int       Session.MaxTimeFrame=240;     // Highest TF to show lines in 1,5,15,30,60,240,1440 etc
//extern bool    Session.EnableDST=false;      // Daylight savings time. Add 1 hour to the open & close hours

extern string    Range.Info="<< Session Range >>";
extern bool      Range.EnableAlarm=false;      // Alarm for when the range exceeds the range thresholdvalue
extern int       Range.AlarmThreshold=40;      // Range alarm threshold 
extern bool      Range.ShowLabel=true;         // Show label with current channel range
extern color     Range.LabelColor=Khaki;       // Show label with current channel range

extern string    OpenVLine.Info="<< Vertical line on open bar >>";
extern bool      OpenVLine.Show=true;          // Draw a vertical line to show the session open time
extern color     OpenVLine.Color=Gold;         // Color of the session open time line
extern int       OpenVLine.Style=2;            // Style of the session open time line: Value 0-4
extern int       OpenVLine.Width=1;            // Width of the session open time line
extern string    OpenVLine.Label="Session Open"; // Label for session open time line

extern string    CloseVLine.Info="<< Vertical line on close bar>>";
extern bool      CloseVLine.Show=true;         // Draw a vertical line to show the session close time
extern color     CloseVLine.Color=Gold;        // Color of the session close time line
extern int       CloseVLine.Style=2;           // Style of the session close time line: Value 0-4
extern int       CloseVLine.Width=1;           // Width of the session close time line
extern string    CloseVLine.Label="Session Close";  // Label for session close time line 

extern string    HighLowLine.Info="<< Session High/Low lines >>";
extern bool      HighLowLine.Show=true;        // Draw a vertical line to show the session close time
extern int       HighLowLine.EndTime=22;       // Time to stop drawing the line
extern color     HighLowLine.Color=Gold;       // Color of the session close time line
extern int       HighLowLine.Style=2;          // Style of the session close time line: Value 0-4
extern int       HighLowLine.Width=1;          // Width of the session close time line

// Indicator buffers
double miaHiBuffer[];
double miaLoBuffer[];

// Indicator module/global variables
bool mbRunOnce;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
//---- indicators
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,miaHiBuffer);
   SetIndexLabel(0,"Asian Session High");
   SetIndexEmptyValue(0,EMPTY_VALUE);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(1,miaLoBuffer);
   SetIndexLabel(1, "Asian Session Low");
   SetIndexEmptyValue(1,EMPTY_VALUE);
      
   //---- Set Indicator Name   
   IndicatorShortName(INDICATOR_NAME+Session.IndicatorId+" "+INDICATOR_VERSION);
   mbRunOnce=false;   
   return(0);
}


//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit() {
  // Clear objects
  for(int i=ObjectsTotal()-1; i>-1; i--)
    if (StringFind(ObjectName(i),INDICATOR_NAME+Session.IndicatorId)>=0)  ObjectDelete(ObjectName(i));
  return(0);
}


//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {
  int iNewTicks;
  int iCountedBars;   
 
  // Get unprocessed ticks
  iCountedBars=IndicatorCounted();
  if(iCountedBars < 0) return (-1);
  iNewTicks=Bars-iCountedBars;
  
  if (Period()>Session.MaxTimeFrame) return(0);
  
  // Draw old sessions
  if (mbRunOnce==false) {
    DrawPreviousSessions();   
    mbRunOnce=true;
  } //endif
  
  // Draw current session
  DrawCurrentSession(iNewTicks);
  
  // Exit
  return(0);
} //endfunction
//+------------------------------------------------------------------+


//-----------------------------------------------------------------------------
// function: GetSessionBars()
// Description: Get nr of bars for the session. There can be bars missing!
//-----------------------------------------------------------------------------
int GetSessionBars(int iSessionOpen, int iSessionClose, int iShift) {
  int i;
  int iNrOfBars=0;
  
  // Check DST
  // if (EnableDST==true) { 
  //   iSessionOpen=iSessionOpen+1;
  //   iSessionClose=iSessionClose+1;
  // }
  
  // Get nr of bars for the session.
  for (i=iShift; i<(Bars-iShift); i++) {    
    // Check for beginning hour
    if (TimeHour(Time[i])==iSessionOpen && TimeMinute(Time[i])==0)  {
      return(iNrOfBars);
    }
    
    // Double check to account for missing bars and weekends
    if (IsSessionActive(iSessionOpen,iSessionClose,Time[i])==false) {
      iNrOfBars--; // Break in data. Remove last bar.
      return(iNrOfBars);
    }
    
    iNrOfBars++;
  } // endfor
  // Unable to complete. Return 0.
  return(0);
}


//-----------------------------------------------------------------------------
// function: DrawCurrentSession()
// Description: Draw lines for current session.
//-----------------------------------------------------------------------------
void DrawCurrentSession(int iNewTicks) {
  int i,j;
  int iNrOfBars;
  double dSessionHigh;
  double dSessionLow;
  static int iRange=0;
  static bool bRangeAlarm=false;
  static bool bSessionActive=false;
 
  if (IsSessionActive(Session.OpenHour,Session.CloseHour-1,Time[0])==true) {
    
    if (bSessionActive==false) bSessionActive=true;
    
    // Get nr of bars for the session. There can be bars missing!
    iNrOfBars=GetSessionBars(Session.OpenHour,Session.CloseHour-1,0);
    
    // Find the highest and lowest data for specified nr of bars
    dSessionHigh=High[iHighest(NULL,0,MODE_HIGH,iNrOfBars+1,0)];
    dSessionLow=Low[iLowest(NULL,0,MODE_LOW,iNrOfBars+1,0)];

    // Draw session lines
    for(i=0; i<=iNrOfBars; i++) {
      miaHiBuffer[i]=dSessionHigh;
      miaLoBuffer[i]=dSessionLow;
    } //endfor
    
    // Draw lines
    if (OpenVLine.Show==true) {
      DrawLine(Time[iNrOfBars],OpenVLine.Style,OpenVLine.Width,OpenVLine.Color,"Open");
      if (StringLen(OpenVLine.Label)>0)
        DrawTextLabel(Time[iNrOfBars],OpenVLine.Label,OpenVLine.Color);
    }
     
    // Draw range text label 
    if (Range.ShowLabel==true) { 
      DrawRangeValue(PriceToPips(dSessionHigh-dSessionLow),Time[iNrOfBars/2],dSessionHigh,Range.LabelColor);
    }
    
    // Range alert
    iRange=PriceToPips(dSessionHigh-dSessionLow);
    if (iRange>Range.AlarmThreshold && Range.EnableAlarm==true) {
      if (bRangeAlarm==false) {
        Alert(INDICATOR_NAME+Session.IndicatorId,", ",Symbol(),", Range: ",iRange);
        bRangeAlarm=true;
      } //endif
    } 
    else {
      bRangeAlarm=false;  
    } //endif
  } //endif
  else {
  
    if (bSessionActive==true) {
      bSessionActive=false;
      DrawPreviousSessions();
    }
  }
} //endfunction


//-----------------------------------------------------------------------------
// function: DrawPreviousSessions()
// Description: Draw lines for previous days sessions in chart.
//-----------------------------------------------------------------------------
void DrawPreviousSessions() {
  int i,j;
  int iNrOfBars;
  int iCloseOnHour;
  double dSessionHigh;
  double dSessionLow;
  int iNrOfDays=0;
  string sLineId;
  string sRange;
  datetime tEndTime;
  
  // Clear the indicator buffers
  for (i=0; i<Bars; i++) {
    miaHiBuffer[j]=EMPTY_VALUE;
    miaLoBuffer[j]=EMPTY_VALUE;   
  }
 
  // Set the closing bar. On hour bar or on previous bar.
  if (Session.CloseOnHour==True) 
    iCloseOnHour=0;
  else
    iCloseOnHour=1;
  
  // Draw asian session for old data
  i=0;
  while (i<Bars && iNrOfDays<Session.NrOfDays) {
    if (TimeHour(Time[i])==Session.CloseHour && TimeMinute(Time[i])==0) {
        
      // Get nr of bars for the session. There can be bars missing!
      iNrOfBars=GetSessionBars(Session.OpenHour,Session.CloseHour,i);
            
      // Find the highest and lowest data for specified nr of bars
      dSessionHigh=High[iHighest(NULL,0,MODE_HIGH,iNrOfBars,i+1)];
      dSessionLow=Low[iLowest(NULL,0,MODE_LOW,iNrOfBars,i+1)];
      
      // Draw session lines
      for(j=i+iCloseOnHour; j<=i+iNrOfBars; j++) {    
        miaHiBuffer[j]=dSessionHigh;
        miaLoBuffer[j]=dSessionLow;    
      } //endfor    
      
      // Draw lines
      if (OpenVLine.Show==true) {
        DrawLine(Time[i+iNrOfBars],OpenVLine.Style,OpenVLine.Width,OpenVLine.Color,"Open");
        if (StringLen(OpenVLine.Label)>0)
          DrawTextLabel(Time[i+iNrOfBars],OpenVLine.Label,OpenVLine.Color);
      }
      if (CloseVLine.Show==true) {
        DrawLine(Time[i]-Period()*60*iCloseOnHour,CloseVLine.Style,CloseVLine.Width,CloseVLine.Color,"Close");
        if (StringLen(CloseVLine.Label)>0)
          DrawTextLabel(Time[i]-Period()*60*iCloseOnHour,CloseVLine.Label,CloseVLine.Color);
      }
      
      // Draw range text label 
      if (Range.ShowLabel==true) { 
        DrawRangeValue(PriceToPips(dSessionHigh-dSessionLow),Time[i+iNrOfBars/2],dSessionHigh,Range.LabelColor);
      }
      
      // Draw session High/Low lines
      if (HighLowLine.Show==true) { 
        tEndTime=StrToTime(TimeToStr(Time[i],TIME_DATE)+" "+HighLowLine.EndTime+":00");
        DrawHLine(dSessionHigh, Time[i]-Period()*60, tEndTime, "High"+iNrOfDays+1, HighLowLine.Style, HighLowLine.Width, HighLowLine.Color);
        DrawHLine(dSessionLow, Time[i]-Period()*60, tEndTime, "Low"+iNrOfDays+1, HighLowLine.Style, HighLowLine.Width, HighLowLine.Color);
      }

      
      iNrOfDays++;  
    } //endif
  i++;
  } //end while
}


//-----------------------------------------------------------------------------
// function: DrawRangeValue()
// Description: Draw range text label below the high channel line
//-----------------------------------------------------------------------------
int DrawRangeValue(double dRange, double tTime, double dPrice, color cTextColor) {
  double tTextPos=0;
  string sLineId;
  string sRange;
  
  sRange=DoubleToStr(dRange,PipDigits());
  sLineId=INDICATOR_NAME+Session.IndicatorId+"_Range_"+TimeToStr(tTime,TIME_DATE );
  
  if (ObjectFind(sLineId)>=0 ) ObjectDelete(sLineId);      
  ObjectCreate(sLineId, OBJ_TEXT, 0, tTime, dPrice); 
  ObjectSet(sLineId, OBJPROP_BACK, false);
  ObjectSetText(sLineId, sRange , 8, "Arial", cTextColor);
  return(0);
}


//-----------------------------------------------------------------------------
// function: IsSessionActive()
// Description: Check if session is open. If DST is enabled add 1hr to the market time
//-----------------------------------------------------------------------------
int IsSessionActive(int iSessionOpen, int iSessionClose, datetime dBarTime) {
   int iBarHour; 
   int iBarMinute;
   bool bResult;
   iBarHour = TimeHour(dBarTime);
   iBarMinute = TimeMinute(dBarTime);
   
   // Check DST
   //if (EnableDST==true) { 
   //  iSessionOpen=iSessionOpen+1;
   //  iSessionClose=iSessionClose+1;
   //}
   
   // Check if market is open.
   if (iSessionOpen<iSessionClose) { 
      if (iBarHour>=iSessionOpen && iBarHour<=iSessionClose) 
        bResult=true; // Open & close before midnight
      else 
        bResult=false;
   }   
   else {  
     if (iBarHour>=iSessionOpen || iBarHour<=iSessionClose) 
       bResult=true; // Open before midnight and close after midnight
     else 
       bResult=false;
   }
   return(bResult);     
}


//-----------------------------------------------------------------------------
// function: DrawLine()
// Description: Draw a horizontal line at specific price
//----------------------------------------------------------------------------- 
int DrawLine(double tTime, int iLineStyle, int iLineWidth, color cLineColor, string sId) {
  string sLineId;
  
  // Set Line object ID  
  sLineId=INDICATOR_NAME+Session.IndicatorId+"_"+sId+"_"+TimeToStr(tTime,TIME_DATE );
  
  // Draw line
  if (ObjectFind(sLineId)>=0 ) ObjectDelete(sLineId);
  ObjectCreate(sLineId, OBJ_TREND, 0, tTime, 0, tTime, 10); 
  //ObjectCreate(sLineId, OBJ_VLINE, 0, tTime, 0); 
  ObjectSet(sLineId, OBJPROP_STYLE, iLineStyle);     
  ObjectSet(sLineId, OBJPROP_WIDTH, iLineWidth);
  ObjectSet(sLineId, OBJPROP_BACK, true);
  ObjectSet(sLineId, OBJPROP_COLOR, cLineColor);    
  return(0);
}

//-----------------------------------------------------------------------------
// function: DrawTextLabel()
// Description: Draw a text label for a line
//-----------------------------------------------------------------------------
int DrawTextLabel(double tTime, string sLabel, color cLineColor) {
  double tTextPos=0;
  string sLineLabel="";
  string sLineId;
  color cTextColor;
  
  // Set Line object ID  
  sLineId=INDICATOR_NAME+Session.IndicatorId+"_"+sLabel+"_"+TimeToStr(tTime,TIME_DATE );
  
  //Set position of text label
  tTextPos=WindowPriceMin()+(WindowPriceMax()-WindowPriceMin())/2;
  //PrintD("tTextPos: "+tTextPos);
  // Draw or text label  
  if (ObjectFind(sLineId)>=0 ) ObjectDelete(sLineId);      
  ObjectCreate(sLineId, OBJ_TEXT, 0, tTime, tTextPos);    
  ObjectSet(sLineId, OBJPROP_ANGLE, 90);
  ObjectSet(sLineId, OBJPROP_BACK, true);
  ObjectSetText(sLineId, sLabel , 8, "Arial", cLineColor);
 
  return(0);
}


//-----------------------------------------------------------------------------
// function: DrawHLine()
// Description: Draw a horizontal line at specific price
//----------------------------------------------------------------------------- 
int DrawHLine(double dPrice, datetime tStart, datetime tEnd, string sLineId, int iLineStyle, int iLineWidth, color cLineColor) {
  
  // Set Line object ID  
  sLineId=INDICATOR_NAME+Session.IndicatorId+"_"+sLineId;
  
  // Draw line
  if (ObjectFind(sLineId)>=0 ) ObjectDelete(sLineId);
  ObjectCreate(sLineId, OBJ_TREND, 0, tStart, dPrice, tEnd, dPrice); 
  ObjectSet(sLineId, OBJPROP_STYLE, iLineStyle);     
  ObjectSet(sLineId, OBJPROP_WIDTH, iLineWidth);
  ObjectSet(sLineId, OBJPROP_BACK, true);
  ObjectSet(sLineId, OBJPROP_RAY, false);  
  ObjectSet(sLineId, OBJPROP_COLOR, cLineColor);    
  return(0);
}


//-----------------------------------------------------------------------------
// function: PriceToPips()
// Description: Convert a proce difference to pips.
//-----------------------------------------------------------------------------
double PriceToPips(double dPrice) {

  if (Digits==2 || Digits==3) 
    return(dPrice/0.01); 
  else if (Digits==4 || Digits==5) 
    return(dPrice/0.0001); 
  else
    return(dPrice);            
} // end funcion()


//-----------------------------------------------------------------------------
// function: PipDigits()
// Description: Digits of the pips
//-----------------------------------------------------------------------------
double PipDigits() {

 if (Digits==3 || Digits==5) 
    return(1); 
  else if (Digits==2 || Digits==4) 
    return(0); 
  else
    return(0);            
} // end funcion()


