//+------------------------------------------------------------------+
//|                                            Day Phases v1.2.8.mq4 |
//|                          Copyright © 2009, 2010 alx@novusorsa.pl |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2009, 2010, alx@novusorsa.pl"
#property link      ""

// Thanks to Tom Clayson for adding Trend Line option

extern bool MasterMode = false;
extern int BrokerToGMT = 2; // manual settings in case autommatic detecton fails.
extern string BrokerGoesOnDST	= "2010.03.14 02:00"; // US Broker settings.
extern string BrokerGoesOffDST	= "2010.11.07 02:00"; // US Broker settings.
extern int DaysBack = 10;
extern int StayAwayMinutesBefore = 5;
extern int StayAwayMinutesAfter = 5;
extern bool showPhaseDescriptions = false; // only if shown as lines
// Tokyo specific settings
extern string Tokyo_Specific_Settings = "=============";
extern bool showTokyoAsLines 		= true; // if false It will display lines
extern color Tokyo_Open 			= C'75,150,75'; //"Tokyo Open";
extern color Tokyo_Lunchtime 		= C'150,75,75'; //"Tokyo lunch time";
extern color Tokyo_Afternoon		= C'75,75,150'; //"Tokyo afternoon";
// Europe specific settings
extern string Europe_Specific_Settings = "=============";
extern bool showEuropeAsLines = false; // if false It will display lines
extern color Europe_EarlyMorning	= C'30,30,30'; //"Pre-Market - preparations";
extern color Europe_DAX_Open		= C'30,30,50'; //"DAX Open";
extern color Europe_Morning			= C'30,50,50'; //"Euro Open";
extern color Europe_LondonAction	= C'00,50,00'; //"London time";
extern color Europe_LiborFix		= C'80,40,00'; //"LIBOR fix";
// US specific settings
extern string US_Specific_Settings = "=============";
extern bool showUSasLines 		= false; // if false It will display lines
extern bool	showUsOpenRange 	= true;
extern int UsOpenRangeLineWidth = 3;
extern color UsOpenRangeLineColor = Orange;
extern color US_EarlyMorning	= C'30,30,30'; 	//"Pre-Market - preparations";
extern color US_Open			= C'20,20,70'; 	//"US Open, opening drive - locust is taking positions";
extern color US_1030_Reversal	= C'30,70,30'; 	//"10:30 reversal - watch out!";
extern color US_LunchTime		= C'50,0,0'; 	//"Lunch time - on low volume!";
extern color US_AfterLunch		= C'50,50,50';	//"return from lunch - new game is about to open";
extern color US_Afternoon		= C'30,30,50';	//"Afternoon session - forget about morning and get ready!";
extern color US_Final			= C'30,50,30';	//"Final moves - last chance to make profit!";
extern color US_GameOver		= C'80,40,00';	//"Close of Cash - don't get caught!";
extern string Other_Settings = "=============";
extern bool	allowLinesAcrossPanes	= true; // if true it will draw Vertical Line, when false it will draw Trendline vertically. VLines intersect with subwindows while Trendlines not.
extern bool EnableAlarm = false;
extern int FireAlarmMinutesBefore = 5;

extern string V_3.0.14;

// This is experimental stuff...
//extern 
bool ShowRegressionChannel = false;
//extern
bool showRegAsLines = false;
//extern 
color regrChanColor = DarkGray;//C'10,10,10';

//...................
#define TokyoPhaseCount 3
#define TokyoNameCount 4 // I need additional name for Nikkei Close line.

datetime TokyoPhaseTime[TokyoPhaseCount][2];
color TokyoColorTable[TokyoPhaseCount];
string TokyoNameTable[TokyoNameCount][2];

#define EuropePhaseCount 5
#define EuropeNameCount 6

datetime EuropePhaseTime[EuropePhaseCount][2];
color EuropeColorTable[EuropePhaseCount];
string EuropeNameTable[EuropeNameCount][2];

#define USphaseCount 8
#define USnameCount 9

datetime USphaseTime[USphaseCount][2];
color UScolorTable[USphaseCount];
string USnameTable[USnameCount][2];
//...................

int stayAwaySecondsBefore, stayAwaySecondsAfter;

#define startTime 0
#define endTime 1

int dayIndex;
datetime alarmFired;
datetime previousBar = 0, currentBar = 0;

double rangeHigh  = 0.0;
double rangeLow  = 0.0;
bool updateEveryTick = false;
	
string PhaseName = "Day_Phases_";
string lineTable[];

#import "kernel32.dll"
void GetSystemTime(int& TimeArray[]);
#import

#define dayInSeconds 86400

#define LondonDefaultTZ		0
#define TokyoDefaultTZ		9
#define NewYorkDefaultTZ	-5
#define SydneyDefaultTZ    10
int BrokerDefaultTZ = 0;
int brokerDSToffset = 0;
int BrokerOffset;

string GvTzName = "Session Phases v1.1 Last known Broker TimeZone";
string GvDstOnName = "Session Phases Broker DST On";
string GvDstOffName = "Session Phases Broker DST Off";
string GvMasterIDName = "Session Phases Master Flag";
string GvMasterName = "Session Phases Master Flag";
string masterID;
bool standalone = false;

#define startupPeriod 4 // for the first 3 minutes I will be updating objects every minute as history data may be incomplete
int startupCount = -1;
bool forceUpdate = false;
datetime previousBrokerTime = 0, currentBrokerTime = 0;
int currentBarCount, previousBarCount;
bool newM1bar = false;
double toopFloor;

#property indicator_chart_window
//------------------------------------------------------
bool isEuropeOnDST(datetime now)
{
	// Europe DST rule:
	// On from 2:00 AM the last Sunday in March
	// Off to  3:00 AM the last Sunday in October
	
	int StartMonth	=	3;
	int EndMonth	=	10;
	int StartWeekDay=	0;
	int EndWeekDay	=	0;
	int StartHour	=	2;
	int EndHour		=	3;
	
	// the above transition times are local time. That means DST starts at 2AM local London standard time and ends at 3AM local London DST time...
	// I need to readjust it to gmt reference. London is GMT so I only need to adjust the end hour.
	EndHour -= 1;

	datetime pointer;
	int month = TimeMonth(now);
	if (month > StartMonth && month < EndMonth) return (true); // easy filter
	if (month < StartMonth || month > EndMonth) return (false); // same deal
	
	int i;
	int lookedDayNum;
	int currentDayNum = TimeDay(now);
	if (month == StartMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(StartMonth+1),".01")); // 1st April
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == StartWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (true);
							if (lookedDayNum > currentDayNum) return (false);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (true); else return (false);
								}
						}	
					pointer -= dayInSeconds; // day in seconds
				}
			return(false);
		}
	
	if (month == EndMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(EndMonth+1),".01")); // 1st November
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == EndWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (false);
							if (lookedDayNum > currentDayNum) return (true);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (false); else return (true);
								}
						}
					pointer -= dayInSeconds; // day in seconds
				}
			return(true);
		}	
	return (false);
}
//------------------------------------------------------ 
bool isUsOnDST(datetime now)
{
	// US DST rule:
	// On from 2:00 AM the second Sunday in March
	// Off to  2:00 AM the first Sunday in November
	
	// the code is almost the same as above. The only difference is I'm looking for the first and second Sunday.
	// and transition times adjustment..
	// this should be put in some kind of universal function, but i have no time, so copy&paste&edit:
	
	int StartMonth	=	3;
	int EndMonth	=	11;
	int StartWeekDay=	0;
	int EndWeekDay	=	0;
	int StartHour	=	2;
	int EndHour		=	2;
	
	// the above transition times are local time. That means DST starts at 2AM local London standard time and ends at 3AM local London DST time...
	// I need to readjust the gmt reference
	// this time a bit complex as NY is GMT-5...
	// I'm convertig the gmt to EST
	now += NewYorkDefaultTZ * 3600;
	
	datetime pointer;
	int month = TimeMonth(now);
	if (month > StartMonth && month < EndMonth) return (true); // easy filter
	if (month < StartMonth || month > EndMonth) return (false); // same deal

	int i;
	int lookedDayNum;
	int currentDayNum = TimeDay(now);
	if (month == StartMonth)
		{
			//now wee need to know the starting day number. This is the second Sunday so lets go forward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(StartMonth),".01")); // 1st March
			//pointer -= dayInSeconds;
			bool firstSundaySpot = false;
			for (i=0;i<14;i++)
				{
					if (TimeDayOfWeek(pointer) == StartWeekDay)
						if (!firstSundaySpot) firstSundaySpot = true;
						else
							{
								lookedDayNum = TimeDay(pointer);
								if (lookedDayNum < currentDayNum) return (true);
								if (lookedDayNum > currentDayNum) return (false);
								if (lookedDayNum == currentDayNum)
									{
										if (TimeHour(now) >= StartHour) return (true); else return (false);
									}
							}	
					pointer += dayInSeconds; // day in seconds
				}
			return(false);
		}
	
	if (month == EndMonth)
		{
			//now wee need to know the starting day number. This is the first Sunday so lets go forward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(EndMonth),".01")); // 1st November
			//pointer -= dayInSeconds;
			
			for (i=0;i<7;i++)
				{
					if (TimeDayOfWeek(pointer) == EndWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (false);
							if (lookedDayNum > currentDayNum) return (true);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (false); else return (true);
								}
						}
					pointer += dayInSeconds; // day in seconds
				}
			return(true);
		}	
	return (false);
}
//------------------------------------------------------ 
bool isJapanOnDST(datetime now)
{
	// DST rule
	// No rule. No DST support. But maybe one day you could put the rule here...
	return (false);
}
//------------------------------------------------------ 
bool isSydneyOnDST(datetime now)
{
	// Sydney DST rule:
	// On from 2:00 AM the last Sunday in October
	// Off to  3:00 AM the last Sunday in March
	
	int StartMonth	=	10;
	int EndMonth	=	3;
	int StartWeekDay=	0;
	int EndWeekDay	=	0;
	int StartHour	=	2;
	int EndHour		=	3;
	
	// the above transition times are local time. That means DST starts at 2AM local Sydney standard time and ends at 3AM local Sydney DST time...
	// I need to readjust it to gmt reference. 
	// Same deal as with New York, shift gmt to make it local Sydney time
	now += SydneyDefaultTZ * 3600;
	//EndHour -= 1;

	datetime pointer;
	int month = TimeMonth(now);
	if (month > StartMonth || month < EndMonth) return (true); // easy filter
	if (month < StartMonth && month > EndMonth) return (false); // same deal
	
	int i;
	int lookedDayNum;
	int currentDayNum = TimeDay(now);
	if (month == StartMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(StartMonth+1),".01")); // 1st November
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == StartWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (true);
							if (lookedDayNum > currentDayNum) return (false);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (true); else return (false);
								}
						}	
					pointer -= dayInSeconds; // day in seconds
				}
			return(false);
		}
	
	if (month == EndMonth)
		{
			//now wee need to know the starting day number. This is the last Sunday so lets do a walkthrough backward:
			pointer = StrToTime(StringConcatenate(TimeYear(now),".0",(EndMonth+1),".01")); // 1st April
			pointer -= dayInSeconds;
			
			for (i=6;i>=0;i--)
				{
					if (TimeDayOfWeek(pointer) == EndWeekDay)
						{
							lookedDayNum = TimeDay(pointer);
							if (lookedDayNum < currentDayNum) return (false);
							if (lookedDayNum > currentDayNum) return (true);
							if (lookedDayNum == currentDayNum)
								{
									if (TimeHour(now) >= StartHour) return (false); else return (true);
								}
						}
					pointer -= dayInSeconds; // day in seconds
				}
			return(true);
		}	
	return (false);
}
//------------------------------------------------------ 
bool isBrokerOnDST(datetime now)
{
	// It is impossible to automatically detect broker feed DST offset.
	// all I can do is to detect absolute offset and provide additional info about transition times.
	// Not very sophisticated but no other way.
	
	datetime startDate	= StrToTime(BrokerGoesOnDST);
	datetime endDate	= StrToTime(BrokerGoesOffDST);
	
	// some data check
	bool startDateFlag = false, endDateFlag = false;
	
	if (TimeYear(startDate) < Year()-1) startDateFlag = false; // wrong or outdated settings
	else startDateFlag = true;
	if (TimeYear(endDate) < Year()-1) endDateFlag = false; // wrong or outdated settings
	else endDateFlag = true;
		
	if (startDateFlag && endDateFlag) // have both dates entered
		{
			if (startDate < endDate)
				{
					if (now >= startDate && now < endDate) return (true); else return (false);
				}
			else if (startDate > endDate) // possibly the start date is future dst on date or the endDate is old one
				{
					if (now < endDate || now > startDate) return (true);
					else return (false);
				}
			else return (false); 
		}
	
	if (startDateFlag && !endDateFlag) // let's use the start date only but warn the user about the settings
		{
			if (now >= startDate)
				{
					Print("DST end date incorrect. Timing may be affected. Check BrokerGoesOffDST. Expected data format: YYYY.MM.DD HH:MM e.g. 2009.11.01 02:00 Note space between day and hour numbers");
					return (true);
				}
			else return (false);
		}

	if (!startDateFlag && endDateFlag) // let's use the end date only but warn the user about the settings
		{
			if (now < endDate)
				{
					Print("DST end date incorrect. Timing may be affected. Check BrokerGoesOnDST. Expected data format: YYYY.MM.DD HH:MM e.g. 2009.03.08 02:00 Note space between day and hour numbers");
					return (true);
				}
			else return (false);
		}

	return (false);
}
//------------------------------------------------------ 
// the code comes from http://forum.mql4.com/21619
string FormatDateTime(int nYear,int nMonth,int nDay,int nHour,int nMin,int nSec)
{
	string sMonth,sDay,sHour,sMin,sSec = "";
	sMonth=100+nMonth;
	sMonth=StringSubstr(sMonth,1);
	sDay=100+nDay;
	sDay=StringSubstr(sDay,1);
	sHour=100+nHour;
	sHour=StringSubstr(sHour,1);
	sMin=100+nMin;
	sMin=StringSubstr(sMin,1);
	sSec=100+nSec;
	sSec=StringSubstr(sSec,1);
	return(StringConcatenate(nYear,".",sMonth,".",sDay," ",sHour,":",sMin,":",sSec));
}
//------------------------------------------------------ 
// the code comes from http://forum.mql4.com/21619
datetime getGMTime()
{
	/*MS stores time in plain format:	
	typedef struct _SYSTEMTIME {
	  WORD wYear;
	  WORD wMonth;
	  WORD wDayOfWeek;
	  WORD wDay;
	  WORD wHour;
	  WORD wMinute;
	  WORD wSecond;
	  WORD wMilliseconds;
	}SYSTEMTIME, *PSYSTEMTIME;
	*/
	int TimeArray[4];
	int nYear,nMonth,nDay,nHour,nMin,nSec;
	
	GetSystemTime(TimeArray);
	// some bit mask and shif magic to get word walues from dwords..
	nYear=TimeArray[0]&0x0000FFFF;
	nMonth=TimeArray[0]>>16;
	nDay=TimeArray[1]>>16;
	nHour=TimeArray[2]&0x0000FFFF;
	nMin=TimeArray[2]>>16;
	nSec=TimeArray[3]&0x0000FFFF;
	
	// Now, MT4 stores time in second elapsed from the 1st Jan 1970.
	// there are two ways to convert:
	// recalculate the MS time into seconds starting from 1970 or
	// convert it into string MT4 can convert into it's time format.
	// The first method is risky - needs to go around number of February days changing every 4 years...
	// At the moment using method 2.
	
	datetime localTime = TimeLocal();
	int localGMTOffset = nHour-TimeHour(localTime);
	if (localGMTOffset < -11) localGMTOffset += 24;
	else if (localGMTOffset > 13) localGMTOffset -= 24;
	
	//This needs to be cleaned  - no need to convert time to string then string to time only because it may be stored in different numeric format
	string utc = FormatDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
	//Print (utc);
	return (StrToTime(utc));
}
//------------------------------------------------------ 
int getBrokerTZ(datetime gmt)
{
	int gmtHour		= TimeHour(gmt);
	datetime timeCurrent = TimeCurrent(); // Hmm...
	int brokerHour	= TimeHour(timeCurrent);
	int dif = brokerHour - gmtHour;
	if (dif < -11) dif += 24;
	if (dif > 13) dif -= 24;
	// now let's check if it's on DST
	brokerDSToffset = isBrokerOnDST(timeCurrent);
	dif -= brokerDSToffset;
	return(dif);
}
//------------------------------------------------------
void updateTimeSettings()
{
	datetime gmt 	= getGMTime();
	BrokerDefaultTZ	= getBrokerTZ(gmt); // It returns standard offset. DST offset is stored in brokerDSToffset
	BrokerOffset = BrokerDefaultTZ + brokerDSToffset; // For compatibility reasons...
	int hi = iHighest(Symbol(),Period(), MODE_HIGH, WHOLE_ARRAY, 0);
	toopFloor = High[hi];
	toopFloor += toopFloor;
	
	int weekDay = TimeDayOfWeek(TimeLocal()); // if weekend, broker TZ will be read from saved Global Variable if any.
	// however it should be checked once the broker feed is active again on Sunday... especially on Sunday!
	if ((Hour() >= 22 && weekDay == 5) || weekDay == 0 || weekDay == 6)
		{
			// Read saved Broker Offset..
			int newOffset = readGV(GvTzName);
			if (newOffset != EMPTY_VALUE) BrokerOffset = newOffset;
			else
				{
					Print ("No data available over weekend. Please wait for the first tick on Monday to update the Broker Time Zone. Using BrokerToGMT to calculate timetables.");
					BrokerOffset = BrokerToGMT;
				}
			BrokerDefaultTZ = BrokerOffset - brokerDSToffset;
		}
	else
		{	// Write settings if allowed...
			if (MasterMode || standalone)
				{
					if (startupCount < startupPeriod || forceUpdate)
						{
							if(newM1bar || forceUpdate)
								{
									// I need to make sure the history is updated so I set it a bit later...
									Print ("Saving new BrokerOffset ", BrokerOffset);
									GlobalVariableSet(GvTzName, BrokerOffset);
								}
						}
				}
		}
}
//------------------------------------------------------
void updateMasterFlags()
{	
	//Print ("updateMasterFlags...");
	if (MasterMode)
		{
			int test;
			test = GlobalVariableGet(GvDstOnName);
			if (test != StrToTime(BrokerGoesOnDST)) GlobalVariableSet(GvDstOnName, StrToTime(BrokerGoesOnDST));
			test = GlobalVariableGet(GvDstOffName);
			if (test != StrToTime(BrokerGoesOffDST)) GlobalVariableSet(GvDstOffName, StrToTime(BrokerGoesOffDST));
			test = GlobalVariableGet(GvMasterName);
			if (test == 0) GlobalVariableSet(GvMasterName, getGMTime());
			test = GlobalVariableGet(GvMasterIDName);
			if (test != 1) GlobalVariableSet(GvMasterIDName, 1);
		}
}
//------------------------------------------------------
void getColors()
{
	TokyoColorTable[0] = Tokyo_Open;
	TokyoColorTable[1] = Tokyo_Lunchtime;
	TokyoColorTable[2] = Tokyo_Afternoon;

	EuropeColorTable[0] = Europe_EarlyMorning;
	EuropeColorTable[1] = Europe_DAX_Open;
	EuropeColorTable[2] = Europe_Morning;
	EuropeColorTable[3] = Europe_LondonAction;
	EuropeColorTable[4] = Europe_LiborFix;
	
	UScolorTable[0] = US_EarlyMorning;
	UScolorTable[1] = US_Open;
	UScolorTable[2] = US_1030_Reversal;
	UScolorTable[3] = US_LunchTime;
	UScolorTable[4] = US_AfterLunch;
	UScolorTable[5] = US_Afternoon;
	UScolorTable[6] = US_Final;
	UScolorTable[7] = US_GameOver;	
}
// --------------------------------
void newTimeTable()
{
	int i, j;
	datetime today = StrToTime(TimeToStr(TimeLocal(), TIME_DATE));

// TOKYO ================================================
	
	// All times local Tokyo time
	TokyoPhaseTime[0][startTime] = StrToTime("09:00"); TokyoPhaseTime[0][endTime] = StrToTime("11:00");
	TokyoPhaseTime[1][startTime] = StrToTime("11:00"); TokyoPhaseTime[1][endTime] = StrToTime("12:30");
	TokyoPhaseTime[2][startTime] = StrToTime("12:30"); TokyoPhaseTime[2][endTime] = StrToTime("15:00");

	// convert to GMT based on Japan standard time.
	int localOffset = (0-TokyoDefaultTZ)*3600 - today;
	for (i=0; i<TokyoPhaseCount; i++)
		{
			TokyoPhaseTime[i][startTime] = TokyoPhaseTime[i][startTime]	+ localOffset;
			TokyoPhaseTime[i][endTime]	= TokyoPhaseTime[i][endTime]	+ localOffset;
		}
		
// EUROPE ================================================

	// All times GMT based on London standard time
	EuropePhaseTime[0][startTime] = StrToTime("06:00"); EuropePhaseTime[0][endTime] = StrToTime("07:00"); //"Pre-Market - preparations";
	EuropePhaseTime[1][startTime] = StrToTime("07:00"); EuropePhaseTime[1][endTime] = StrToTime("08:00"); //"DAX Open";
	EuropePhaseTime[2][startTime] = StrToTime("08:00"); EuropePhaseTime[2][endTime] = StrToTime("09:00"); //"Euro Open";
	EuropePhaseTime[3][startTime] = StrToTime("09:00"); EuropePhaseTime[3][endTime] = StrToTime("11:00"); //"London Open";
	EuropePhaseTime[4][startTime] = StrToTime("11:00"); EuropePhaseTime[4][endTime] = StrToTime("12:00"); //"London LIBOR fix";

	localOffset = 0 - today;
	for (i=0; i<EuropePhaseCount; i++)
		{
			EuropePhaseTime[i][startTime] = EuropePhaseTime[i][startTime]	+ localOffset;
			EuropePhaseTime[i][endTime]	= EuropePhaseTime[i][endTime]		+ localOffset;	
		}
		
// US ================================================
	// All times NY time
	USphaseTime[0][startTime] = StrToTime("07:00"); USphaseTime[0][endTime] = StrToTime("09:30");
	USphaseTime[1][startTime] = StrToTime("09:30"); USphaseTime[1][endTime] = StrToTime("10:30");
	USphaseTime[2][startTime] = StrToTime("10:30"); USphaseTime[2][endTime] = StrToTime("11:15");
	USphaseTime[3][startTime] = StrToTime("11:30"); USphaseTime[3][endTime] = StrToTime("14:00");
	USphaseTime[4][startTime] = StrToTime("14:00"); USphaseTime[4][endTime] = StrToTime("14:30");
	USphaseTime[5][startTime] = StrToTime("14:30"); USphaseTime[5][endTime] = StrToTime("15:00");
	USphaseTime[6][startTime] = StrToTime("15:00"); USphaseTime[6][endTime] = StrToTime("16:00");
	USphaseTime[7][startTime] = StrToTime("16:00"); USphaseTime[7][endTime] = StrToTime("16:15");
	
	// convert to GMT based on NY standard time.
	localOffset = (0-NewYorkDefaultTZ)*3600 - today;
	for (i=0; i<USphaseCount; i++)
		{
			USphaseTime[i][startTime] = USphaseTime[i][startTime]	+ localOffset;
			USphaseTime[i][endTime]	= USphaseTime[i][endTime]		+ localOffset;
		}
}
// --------------------------------
int init()
{
	masterID = StringConcatenate("Day Phases is Master on ",Symbol(),timeFrameToStr(Period()));
	GvMasterIDName = masterID;
	standalone = false;
	currentBarCount = Bars;
	previousBarCount = currentBarCount;
	
	if(Period() > PERIOD_M15)
		{
			stayAwaySecondsBefore = 0;
			stayAwaySecondsAfter = 0;
		}
	else
		{
			stayAwaySecondsBefore = StayAwayMinutesBefore*60;
			stayAwaySecondsAfter = StayAwayMinutesAfter*60;
		}
	
	updateTimeSettings();
	newTimeTable();
	
	TokyoNameTable[0][0] = PhaseName + "_J1_"; TokyoNameTable[0][1] = "Nikkei Open";
	TokyoNameTable[1][0] = PhaseName + "_J2_"; TokyoNameTable[1][1] = "Nikkei Lunch time Close";
	TokyoNameTable[2][0] = PhaseName + "_J3_"; TokyoNameTable[2][1] = "Nikkei Afternoon Open";
	TokyoNameTable[4][0] = PhaseName + "_J4_"; TokyoNameTable[3][1] = "Nikkei Close";

	EuropeNameTable[0][0] = PhaseName + "_E1_"; EuropeNameTable[0][1] = "Europe Pre-Market";
	EuropeNameTable[1][0] = PhaseName + "_E2_"; EuropeNameTable[1][1] = "Europe DAX Open";
	EuropeNameTable[2][0] = PhaseName + "_E3_"; EuropeNameTable[2][1] = "Euro/London Open";
	EuropeNameTable[3][0] = PhaseName + "_E4_"; EuropeNameTable[3][1] = "Europe London is in action";
	EuropeNameTable[4][0] = PhaseName + "_E5_"; EuropeNameTable[4][1] = "Europe LIBOR fix";
	EuropeNameTable[5][0] = PhaseName + "_E5_"; EuropeNameTable[5][1] = "Europe London Close";
	
	USnameTable[0][0] = PhaseName + "_U1_"; USnameTable[0][1] = "US Pre-Market - London taking profit";
	USnameTable[1][0] = PhaseName + "_U2_"; USnameTable[1][1] = "US Opening drive";
	USnameTable[2][0] = PhaseName + "_U3_"; USnameTable[2][1] = "US 10:30 reversal - watch out!";
	USnameTable[3][0] = PhaseName + "_U4_"; USnameTable[3][1] = "US Lunch time - profit time!";
	USnameTable[4][0] = PhaseName + "_U5_"; USnameTable[4][1] = "US Returns from lunch - new game begins!";
	USnameTable[5][0] = PhaseName + "_U6_"; USnameTable[5][1] = "US Afternoon session";
	USnameTable[6][0] = PhaseName + "_U7_"; USnameTable[6][1] = "US Final moves - last chance to make profit!";
	USnameTable[7][0] = PhaseName + "_U8_"; USnameTable[7][1] = "US Close of Cash - don't get caught!";
	USnameTable[8][0] = PhaseName + "_U9_"; USnameTable[8][1] = "US Final Close";
	
	getColors();
	
	currentBar = Time[0];
	previousBar = currentBar;
	
	deleteObjects();
	createObjects();
	return(0);
}
// --------------------------------
int deinit()
{
	deleteObjects();
	if (MasterMode)
		{
			GlobalVariableDel(GvMasterName);
			GlobalVariableDel(GvMasterIDName);
			
			GlobalVariableDel(GvDstOnName);
			GlobalVariableDel(GvDstOffName);		
		}
	return(0);
}
// --------------------------------
void createObjects()
{
	string name;
	int i;
	for (dayIndex = 0; dayIndex < DaysBack+1; dayIndex++)
		{
// Now, the timetable is GMT. I need to include local time DST, broker time offset and broker DST if any...

			int dayOffset = iTime(Symbol(), PERIOD_D1, dayIndex);
			// strip the dayOffset from hours, minutes and seconds
			dayOffset -= TimeHour(dayOffset)*3600 + TimeMinute(dayOffset)*60 + TimeSeconds(dayOffset);			
			dayOffset += (BrokerDefaultTZ + isBrokerOnDST(dayOffset))*3600;
			int sessionOffset;
			datetime startDateTime, endDateTime;
			
		// TOKYO
			sessionOffset = dayOffset - isJapanOnDST(dayOffset)*3600;
			for (i=0; i< TokyoPhaseCount; i++)
				{
					name = TokyoNameTable[i][0] + dayIndex;
					startDateTime 	= TokyoPhaseTime[i][startTime] + sessionOffset;
					endDateTime 	= TokyoPhaseTime[i][endTime] + sessionOffset;

					// some issue with lines appearing on Sudnay...
					if ((TimeDayOfWeek(startDateTime) > 0 || (TimeDayOfWeek(startDateTime) == 0 && TimeHour(startDateTime) >= 18)) && TimeDayOfWeek(startDateTime) <6) // This was to prevent Sunday lines on brokers making them into Sunday bars...
						{
							if (!showTokyoAsLines) drawRect(name, startDateTime+stayAwaySecondsBefore, 0, endDateTime-stayAwaySecondsAfter, toopFloor, TokyoColorTable[i]);
							else
								{
									drawVLine(name+0, startDateTime, TokyoColorTable[i], TokyoNameTable[i][1]);

									if (i==TokyoPhaseCount-1) // This is extra line to mark Tokyo Close time. May get mixed with Europe open...
										{
											// this one is a bit diferent than the rest, mostly because it was created with a bit different approach. Anyway, NIKKEI close.
											drawVLine(name+1, endDateTime, TokyoColorTable[i], TokyoNameTable[i+1][1]);
										}						
								}
							
							if(ShowRegressionChannel) drawLReg("LReg "+name, startDateTime, endDateTime, TokyoColorTable[i]);
						}
				}
				
		// EUROPE
			sessionOffset = dayOffset - isEuropeOnDST(dayOffset)*3600;
			for (i=0; i< EuropePhaseCount; i++)
				{
					name = EuropeNameTable[i][0] + dayIndex;

					startDateTime 	= EuropePhaseTime[i][startTime] + sessionOffset;
					endDateTime 	= EuropePhaseTime[i][endTime] + sessionOffset;
						
					if (!showEuropeAsLines) drawRect(name, startDateTime+stayAwaySecondsBefore, 0, endDateTime-stayAwaySecondsAfter, toopFloor, EuropeColorTable[i]);
					else
						{
							drawVLine(name+0, startDateTime, EuropeColorTable[i], EuropeNameTable[i][1]);

							if (i==EuropePhaseCount-1) // This is extra line to mark Europe Close time. Actually FTSE Close 1600 London time. May get mixed with US Open...
								{
									endDateTime = startDateTime + 14400; // last start time is LIBOR fix 1200. We need 1600, 4*3600 seconds.
									drawVLine(name+1, endDateTime, EuropeColorTable[i], EuropeNameTable[i+1][1]);
								}
						}
			
					if(ShowRegressionChannel) drawLReg("LReg "+name, startDateTime, endDateTime, EuropeColorTable[i]);
				}
				
		// US
			sessionOffset = dayOffset - isUsOnDST(dayOffset)*3600;
			for (i=0; i< USphaseCount; i++)
				{
					name = USnameTable[i][0] + dayIndex;

					startDateTime 	= USphaseTime[i][startTime] + sessionOffset;
					endDateTime 	= USphaseTime[i][endTime] + sessionOffset;
										
					if (!showUSasLines) drawRect(name, startDateTime+stayAwaySecondsBefore, 0, endDateTime-stayAwaySecondsAfter, toopFloor, UScolorTable[i]);
					else
						{
							drawVLine(name+0, startDateTime, UScolorTable[i], USnameTable[i][1]);
							
							if (i==USphaseCount-1) // This is extra line to mark Europe Close time. May get mixed with Europe open...
								{
									endDateTime = startDateTime + 3600; // last start time is 1600 NY time. In general there is nothing to do about 1630 but let's make it 1700
									drawVLine(name+1, endDateTime, UScolorTable[i], USnameTable[i+1][1]);
								}
						}
						
					if(ShowRegressionChannel)
						{
							// Now, I will consolidate some of original SP phases. I mean the phases after lunch time
							if (i == 4 || i== 7 || i== 2) endDateTime = startDateTime+3600-(StayAwayMinutesAfter+StayAwayMinutesBefore)*60;
							if (i != 5) drawLReg("LReg "+name, startDateTime, endDateTime, UScolorTable[i]);
						}
						
					if (showUsOpenRange)
						{
							if (i == 1 && getBCrange(startDateTime, startDateTime + 1800))
								{
									datetime rangeEndTime = USphaseTime[7][endTime] + sessionOffset;
									drawBCrange(name+"BCrangeHigh", rangeHigh, startDateTime, rangeEndTime);
									drawBCrange(name+"BCrangeLow", rangeLow, startDateTime,  rangeEndTime);									
								}
						}
				}
		}

}
// --------------------------------
void updateObjects()
{
	currentBar = iTime(Symbol(), PERIOD_H1,0); // once an hour should be ok. iTime(Symbol(), PERIOD_D1,0);

	// During US Open update BC range...
	if (showUsOpenRange)
		{
			// This is exceptional but no need to update all objects...
			datetime dayOffset = iTime(Symbol(), PERIOD_D1,0);
			int sessionOffset = dayOffset + (BrokerDefaultTZ + isBrokerOnDST(dayOffset) - isUsOnDST(dayOffset))*3600;
		
			double startDateTime 	= USphaseTime[1][startTime] + sessionOffset;
			double endDateTime 	= USphaseTime[1][endTime] + sessionOffset;
		
			datetime timeCurrent = TimeCurrent();
			if (timeCurrent >= startDateTime && timeCurrent <= endDateTime) // updateEveryTick = true;
				{
					if (getBCrange(startDateTime, startDateTime + 1800))
						{
							string name = USnameTable[1][0] + 0; // current day and phase name
							datetime rangeEndTime = USphaseTime[7][endTime] + sessionOffset;
							drawBCrange(name+"BCrangeHigh", rangeHigh, startDateTime, rangeEndTime);
							drawBCrange(name+"BCrangeLow", rangeLow, startDateTime,  rangeEndTime);									
						}		
				}
			
		}
	
	bool updateEveryMinute = false;
	if (startupCount < startupPeriod)
		{
			//Print ("startupCount ", startupCount, " startupPeriod ", startupPeriod);
			if (newM1bar) updateEveryMinute = true;	else updateEveryMinute = false;
		}
	else updateEveryMinute = false;
	
	if (currentBar >previousBar || updateEveryMinute || forceUpdate)
		{
			//Print ("updateObjects...  currentBar >previousBar ",currentBar,">",previousBar, " updateEveryMinute ", updateEveryMinute, " forceUpdate ", forceUpdate);
			previousBar = currentBar;
			deleteObjects();
			createObjects();
		}
}
// --------------------------------
void deleteObjects()
{
	string name;
	int i;
	for (dayIndex = 0; dayIndex < DaysBack+1; dayIndex++)
		{
		// TOKYO
			for (i=0; i< TokyoPhaseCount; i++)
				{
					name = TokyoNameTable[i][0] + dayIndex;
					ObjectDelete(name);
					ObjectDelete(name+0);
					ObjectDelete(name+1);
					ObjectDelete("LReg "+name);
				}
		// EUROPE
			for (i=0; i< EuropePhaseCount; i++)
				{
					name = EuropeNameTable[i][0] + dayIndex;
					ObjectDelete(name);
					ObjectDelete(name+0);
					ObjectDelete(name+1);
					ObjectDelete("LReg "+name);
				}
		// US
			for (i=0; i< USphaseCount; i++)
				{
					name = USnameTable[i][0] + dayIndex;
					ObjectDelete(name);
					ObjectDelete(name+0);
					ObjectDelete(name+1);
					ObjectDelete("LReg "+name);
					
					if (showUsOpenRange)
						{
							ObjectDelete(name+"BCrangeHigh");
							ObjectDelete(name+"BCrangeLow");
						}
				}
		}
}
// --------------------------------
bool getBCrange(datetime start, datetime end)
{
	// I look for two 30 minutes bars to find High and Low
	// However, I found the bars H/L may not reflect tops/bottoms on lower timeframes. It all depends on broker feed quality.
	double timeFrame = PERIOD_M30;
	double bar1high = 0, bar2high = 0, bar1low = 0, bar2low = 0;
	
	int startBarIndex = iBarShift(Symbol(), timeFrame, start, true);
	int endBarIndex = iBarShift(Symbol(), timeFrame, end, true);
	
	bar1high = iHigh(Symbol(), timeFrame, startBarIndex);
	bar2high = iHigh(Symbol(), timeFrame, endBarIndex);
	bar1low = iLow(Symbol(), timeFrame, startBarIndex);
	bar2low = iLow(Symbol(), timeFrame, endBarIndex);
	
	if (bar1high <=0 ) return (false);
	
	if (bar2high <=0) bar2high = bar1high;
	if (bar2low <=0) bar2low = bar1low;

	if (bar1high >= bar2high) rangeHigh = bar1high; else rangeHigh = bar2high;
	if (bar1low <= bar2low) rangeLow = bar1low; else rangeLow = bar2low;
	
	return (true);
}
// ---------------------------------
void drawBCrange(string name, double price1, datetime time1, datetime time2) 
{
	ObjectCreate(name, OBJ_TREND, 0, time1, price1, time2, price1);
	// this is to update existing lines
	ObjectSet(name, OBJPROP_PRICE1, price1);
	ObjectSet(name, OBJPROP_PRICE2, price1);
	
	ObjectSet(name, OBJPROP_COLOR, UsOpenRangeLineColor);
	ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
	ObjectSet(name, OBJPROP_WIDTH, UsOpenRangeLineWidth);
	ObjectSet(name, OBJPROP_BACK, true);
	ObjectSet(name, OBJPROP_RAY, false);
	ObjectSetText(name,DoubleToStr(price1, Digits));//,6,"Arial",CLR_NONE);
}
// ---------------------------------
void drawVLine(string name, datetime time1, color lineColor, string description)
{
	// I need to check for overlaping lines. Normally I would not care as ObjectCreate does not allow to create new object with existing name
	// But the names are actually different while lines may overlap so I have to check timestamps
	
	int arraySize = ArraySize(lineTable);
	bool lineExists = false; string duplicatedLineName; int duplicatePosition = -1;
	if (arraySize > 0) // we have something to check
		{
			for (int i=0;i<arraySize;i++)
				{
					duplicatedLineName = lineTable[i];
					datetime lineTimeStamp = ObjectGet(duplicatedLineName, OBJPROP_TIME1);
					if (lineTimeStamp == time1)
						{
							lineExists = true;
							duplicatePosition = i;
							i = arraySize; // exit loop
						}
				}
		}
	
	if (!lineExists)
		{
			ArrayResize(lineTable, arraySize+1);
			lineTable[arraySize] = name;
		}
	// Now, the duplicated line does not have to be the same name as the passed name.
	// I delete the old line but I take its description to merge it with the new line.
	else
		{	
			if (name !=duplicatedLineName)
				{
					description = ObjectDescription(duplicatedLineName) + " -> "+description;
					ObjectDelete(duplicatedLineName);
					lineTable[duplicatePosition] = name;
				}
		}
		
	if (allowLinesAcrossPanes) ObjectCreate(name, OBJ_VLINE, 0, time1, NULL, NULL, NULL, NULL, NULL);
	else
		{
			ObjectCreate(name, OBJ_TREND, 0, time1, 0, time1, 0.5, NULL, NULL);
			ObjectSet(name, OBJPROP_RAY, true);
		}
	ObjectSet(name, OBJPROP_COLOR, lineColor);
	if (showPhaseDescriptions) ObjectSetText(name, description);
	ObjectSet(name, OBJPROP_STYLE, STYLE_DOT);
	ObjectSet(name, OBJPROP_BACK, true);
}
// ---------------------------------
void drawLReg(string name, datetime time1, datetime time2, color regrColor)
{
	//regrColor += regrChanColor;
	regrColor = regrChanColor;
	ObjectCreate(name, OBJ_REGRESSION, 0, time1, NULL, time2, NULL);
	ObjectSet(name, OBJPROP_COLOR, regrColor);
	ObjectSet(name, OBJPROP_BACK, !showRegAsLines);
}
// ---------------------------------
void drawRect(string name, datetime time1, double price1, datetime time2, double price2, color rectColor) 
{
	ObjectCreate(name, OBJ_RECTANGLE, 0, time1, price1, time2, price2);
	ObjectSet(name, OBJPROP_COLOR, rectColor);
	ObjectSet(name, OBJPROP_BACK, true);
}
// --------------------------------
void Alarm(string alarmDescription)
{
	string altertText = alarmDescription;
	Alert(altertText);		
	Print("-----------------------------------");
	altertText = ">>>>>>>  " + altertText;
	Print(altertText);
	Print("-----------------------------------");
}
// --------------------------------
int readGV(string GvName)
{
	int result = EMPTY_VALUE;
	if (GlobalVariableCheck(GvName))
		{
			result = GlobalVariableGet(GvName);
		}
	return (result);
}
// --------------------------------
bool checkUpdateSchedule()
{
	// check if any master exists, if not, save the BrokerOffset as in Master mode.
	int isThereAnyMaster = readGV(GvMasterName);
	if (isThereAnyMaster > 0 && isThereAnyMaster != EMPTY_VALUE) standalone = false; else standalone = true;

	bool update = false;
	int newTZ = readGV(GvTzName);
	if (newTZ != BrokerOffset)
		{
			if (newTZ > -11 && newTZ < 13)
				{
					Print ("Found new BrokerOffset value: ", newTZ, " while BrokerOffset ", BrokerOffset);
					if (!MasterMode && !standalone) BrokerOffset = newTZ;
					update = true;
				}
		}
	// read DST settings, Broker TZ is updated automatically when feed active
	int newStart = readGV(GvDstOnName);
	int newEnd = readGV(GvDstOffName);
	if (newStart != EMPTY_VALUE && newEnd != EMPTY_VALUE && newStart*newEnd != NULL)
		{
			datetime currentStart = StrToTime(BrokerGoesOnDST);
			datetime currentEnd = StrToTime(BrokerGoesOffDST);
			
			if (currentStart != newStart)
				{
					if (!MasterMode && !standalone) BrokerGoesOnDST = TimeToStr(newStart, TIME_DATE|TIME_MINUTES);
					Print ("Found new BrokerGoesOnDST date: ", TimeToStr(newStart, TIME_DATE|TIME_MINUTES));//BrokerGoesOnDST);
					update = true;
				}
				
			if (currentEnd != newEnd)
				{
					if (!MasterMode && !standalone) BrokerGoesOffDST = TimeToStr(newEnd, TIME_DATE|TIME_MINUTES);
					Print ("Found new BrokerGoesOffDST date: ", TimeToStr(newEnd, TIME_DATE|TIME_MINUTES));//BrokerGoesOffDST);
					update = true;
				}
		}
		
	currentBarCount = Bars;
	if (currentBarCount != previousBarCount)
		{
			if (currentBarCount > previousBarCount +1 || currentBarCount < previousBarCount)
				{
					Print ("Discontinued history detected");
					startupCount = -1; // I will have to follow it for a while
					update = true;
				}
			previousBarCount = currentBarCount;
		}

	if (startupCount < startupPeriod) if (newM1bar) startupCount ++;
	
	return(update);
}
// --------------------------------
int start()
{	
	currentBrokerTime = iTime(NULL, PERIOD_M1, 0);
	if (currentBrokerTime >  previousBrokerTime) newM1bar = true; else newM1bar = false;
	updateMasterFlags();
	forceUpdate = checkUpdateSchedule();
	updateTimeSettings();
	updateObjects();
	forceUpdate = false; // clear flag
	previousBrokerTime = currentBrokerTime;
	
	int i;
   	if(EnableAlarm)
   		{
   		// TOKYO
   			for (i=0; i< TokyoPhaseCount; i++)
   				{
   					if(currentBrokerTime == TokyoPhaseTime[i][startTime] - FireAlarmMinutesBefore * 60)
   						{
   							if(alarmFired != currentBrokerTime) 
   								{
   									Alarm(TokyoNameTable[i][1]);
   									alarmFired = currentBrokerTime;
   								}
   						}
   				}
   		// EUROEPE		
			for (i=0; i< EuropePhaseCount; i++)
				{
					if(currentBrokerTime == EuropePhaseTime[i][startTime] - FireAlarmMinutesBefore * 60)
						{
							if(alarmFired != currentBrokerTime) 
								{
									Alarm(EuropeNameTable[i][1]);
									alarmFired = currentBrokerTime;
								}
						}
				}
		// US
   			for (i=0; i< USphaseCount; i++)
   				{
   					if(currentBrokerTime == USphaseTime[i][startTime] - FireAlarmMinutesBefore * 60)
   						{
   							if(alarmFired != currentBrokerTime)
   								{
   									Alarm(USnameTable[i][1]);
   									alarmFired = currentBrokerTime;
   								}
   						}
   				}
   		}
   return(0);
  }
// --------------------------------
string timeFrameToStr(int timeFrame)
{
	string name = "none";
	switch (timeFrame)
		{
			case(PERIOD_M1): name = "M1"; break;
			case(PERIOD_M5): name = "M5"; break;
			case(PERIOD_M15): name = "M15"; break;
			case(PERIOD_M30): name = "M30"; break;
			case(PERIOD_H1): name = "H1"; break;
			case(PERIOD_H4): name = "H4"; break;
			case(PERIOD_D1): name = "D1"; break;
			case(PERIOD_W1): name = "W1"; break;
			case(PERIOD_MN1): name = "MN1"; break;
		}
		
	return(name);	
}