
#define SECONDS_IN_DAY			86400
#define SECONDS_IN_HOUR			3600
#define SECONDS_IN_MINUTE		60

#define SET_DAILY_EST 		1
#define SET_WEEKLY 			2
#define SET_MONTHLY 		3

#define PERIOD_CLOSE		0
#define PERIOD_HIGH			1
#define PERIOD_LOW			2

#property  copyright "Jason Normandin"
#property  link      ""

// Pivot point lines should display in chart window
#property indicator_chart_window

// Seven Lines to display
// In order, buffers are:
// R3, R2, R1, PP, S1, S2, S3
#property indicator_buffers 7

// Indicator line colors
#property indicator_color1 Green
#property indicator_color2 Red
#property indicator_color3 Red
#property indicator_color4 White
#property indicator_color5 Green
#property indicator_color6 Green
#property indicator_color7 Green

// Default Set will be daily eastern close
extern int pivotset	= SET_DAILY_EST;
extern int CloseTime = 2;

// Buffers
double     resist3[];
double     resist2[];
double     resist1[];
double     pivotpoint[];
double     support1[];
double     support2[];
double     support3[];

// Holds Readable Name of Set for line labelling
string periodName;

int init() {

	color linecolor1 = White;
	color linecolor2 = White;
	

	// Set periodName and linecolor
	switch (pivotset) {
		case SET_DAILY_EST:
			periodName = "Daily";
			linecolor1 = Green;
			linecolor2 = Red;
			break;
		case SET_WEEKLY:
			periodName = "Weekly";
			linecolor1 = Green;
			linecolor2 = Red;
			break;
		case SET_MONTHLY:
			periodName = "Monthly";
			linecolor1 = Green;
			linecolor2 = Red;
			break;
	}
	
	// Set indicator name	
	IndicatorShortName(periodName + " Pivots");
	
	// Assign Buffers and their properties
	SetIndexBuffer(0, resist3);
	SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, linecolor1);
	SetIndexLabel(0, periodName + " R3");
	
	SetIndexBuffer(1, resist2);
	SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 3, linecolor2);
	SetIndexLabel(1, periodName + " R2");
	
	SetIndexBuffer(2, resist1);
	SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 2, linecolor2);
	SetIndexLabel(2, periodName + " R1");
	
	SetIndexBuffer(3, pivotpoint);
	SetIndexStyle(3, DRAW_LINE, STYLE_SOLID, 3, White);
	SetIndexLabel(3, periodName + " Pivot");
	
	SetIndexBuffer(4, support1);
	SetIndexStyle(4, DRAW_LINE, STYLE_SOLID, 2, linecolor1);
	SetIndexLabel(4, periodName + " S1");
	
	SetIndexBuffer(5, support2);
	SetIndexStyle(5, DRAW_LINE, STYLE_SOLID, 3, linecolor1);
	SetIndexLabel(5, periodName + " S2");
	
	SetIndexBuffer(6, support3);
	SetIndexStyle(6, DRAW_LINE, STYLE_SOLID, 2, linecolor2);
	SetIndexLabel(6, periodName + " S3");
	
  	return(0);
}

int start() {
   int limit;
   int counted_bars=IndicatorCounted();
   
   // Don't display pivots on charts with timeframe too long
   if (pivotset == SET_DAILY_EST && Period() >= PERIOD_H4) return(0);
   if (pivotset == SET_WEEKLY    && Period() >= PERIOD_W1) return(0);
   if (pivotset == SET_MONTHLY   && Period() >= PERIOD_W1) return(0);
   
   if(counted_bars>0) counted_bars--;
   limit=Bars-counted_bars;

	for(int i=limit-1; i>=0; i--) {

		// array used to fetch price points
		double 		prices[3];
		
		// vars to hold price points`
		double high, low, close;
		
		// vars to hold calculated values
		double pivot, r1, r2, r3, s1, s2, s3;
		
		// timestamp of the bar pivots being calculated for
		datetime 	timestamp = iTime(NULL, 0, i);
		
		// fetch price points based on set
		switch (pivotset) {
			case SET_DAILY_EST:
   	      GetDailyESTPoints(prices, timestamp);
				break;
			case SET_WEEKLY:
				GetWeeklyPoints(prices, timestamp);
				break;
			case SET_MONTHLY:
				GetMonthlyPoints(prices, timestamp);
				break;
		}
		
		// Calculate the pivots
		close = prices[PERIOD_CLOSE];
		high  = prices[PERIOD_HIGH];
		low   = prices[PERIOD_LOW];
		pivot = (high + low + close) / 3;
		r1 = 2 * pivot - low;
		s1 = 2 * pivot - high;
		r2 = pivot + (r1 - s1);
		s2 = pivot - (r1 - s1);
		r3 = high + 2 * (pivot - low);
		s3 = low - 2 * (high - pivot);

		// Place values in Buffers
		resist3[i] = NormalizeDouble(r3,Digits);
		resist2[i] = NormalizeDouble(r2,Digits);
		resist1[i] = NormalizeDouble(r1,Digits);
		pivotpoint[i] = NormalizeDouble(pivot,Digits);
		support1[i] = NormalizeDouble(s1,Digits);
		support2[i] = NormalizeDouble(s2,Digits);
		support3[i] = NormalizeDouble(s3,Digits);
	}

	return(0);
}


void GetDailyESTPoints(double& prices[], datetime timestamp) {
	
	double 		high		= 0;
	double 		low		= 100000;
	double 		close		= 0;
	datetime 	dtCloseTime = GetTimestampPrevUSClose(timestamp);
	int			iBarStart = iBarShift(NULL,PERIOD_H1,dtCloseTime,true) + 24;
	
	// Iterate through the 24 periods, grabbing prices for bars that exist.

	for (int i=iBarStart;i>=iBarStart-23;i--) {
		
		// grab high and low from the hour bar
		double periodHigh = iHigh(NULL, PERIOD_H1, i);
		double periodLow  = iLow (NULL, PERIOD_H1, i);
			
		// set the close to the close of this hour bar
		// if it isn't the last bar, it will be overwritten
		close = iClose(NULL, PERIOD_H1, i);
			
		// if high is still null or lower than period high, replace the value
		high = MathMax(high,periodHigh);
		
		// if low is still null or higher than period low, replace the value
		low = MathMin(low,periodLow);
	}
	
	//Print("Calculated Prices for ", TimeToStr(origtime), "   High: ", high, "   Low: ", low, "   Close: ", close);
		
	// Place the values back into the passed by ref array
  	prices[PERIOD_CLOSE] = close;
  	prices[PERIOD_HIGH]  = high;
   prices[PERIOD_LOW]   = low;

	return;
}



void GetWeeklyPoints(double& prices[], datetime timestamp) {
	
	int		 offset;
	
	// set timestamp to midnight of specified Day
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// subtract days until it's sunday
	timestamp -= TimeDayOfWeek(timestamp) * SECONDS_IN_DAY;
	
	// get the appropriate offset for the previous week
	offset = iBarShift(NULL,PERIOD_W1,timestamp, true) + 1;
	
	double 	high	= iHigh(NULL, PERIOD_W1, offset);
	double 	low	= iLow(NULL, PERIOD_W1, offset);
	double 	close	= iClose(NULL, PERIOD_W1, offset);

	// Place the values back into the passed by ref array
	prices[PERIOD_CLOSE] = close;
	prices[PERIOD_HIGH]  = high;
	prices[PERIOD_LOW]   = low;
	
	return;
}

void GetMonthlyPoints(double& prices[], datetime timestamp) {
	int		 offset;
	
	// set timestamp to midnight of specified Day
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// subtract days until it's the first of the month
	timestamp -= (TimeDay(timestamp) - 1) * SECONDS_IN_DAY;
	
	// get the appropriate offset for the previous month
	offset = iBarShift(NULL,PERIOD_MN1,timestamp, true) + 1;
	
	double 	high	= iHigh(NULL, PERIOD_MN1, offset);
	double 	low	= iLow(NULL, PERIOD_MN1, offset);
	double 	close	= iClose(NULL, PERIOD_MN1, offset);

	// Place the values back into the passed by ref array
   prices[PERIOD_CLOSE] = close;
   prices[PERIOD_HIGH]  = high;
   prices[PERIOD_LOW]   = low;

	return;
}

// Takes a timestamp (in server time) and returns a timestamp (in server time)
// indicating the previous US market close.
// Note:  This still returns 5pm for Fridays!
datetime GetTimestampPrevUSClose(datetime timestamp) {
	
	// Convert to Eastern time so head doesn't hurt.
	timestamp = GetEasternTime(timestamp);
	
	// If it's not yet past 5pm Eastern, must use previous day by subtracting seconds in day
	if (TimeHour(timestamp) < CloseTime)	
		timestamp -= SECONDS_IN_DAY;
	
	// Is timestamp on Sunday?  Subtract a Day
//	if (TimeDayOfWeek(timestamp) == 0)
//		timestamp -= SECONDS_IN_DAY;
		
	// Is dtTimestamp on Saturday?  Subtract a Day
   if (TimeDayOfWeek(timestamp) == 6)
		timestamp -= SECONDS_IN_DAY;
		
	// Strip hours, minutes and seconds from dtTimestamp
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// Add 17 hours back in so it's 5pm
	timestamp += CloseTime * SECONDS_IN_HOUR;
	
	// convert back to server Time
	timestamp = GetServerTimeFromEastern(timestamp);
	
	return (timestamp);
}

datetime GetEasternTime(datetime dtTimestamp) {

	int   iEasternOffset = GlobalVariableGet("EasternTimeOffset");
	return ((dtTimestamp) + (iEasternOffset * SECONDS_IN_HOUR));

}

datetime GetServerTimeFromEastern(datetime dtTimestamp) {

	int   iEasternOffset = GlobalVariableGet("EasternTimeOffset");
	return ((dtTimestamp) - (iEasternOffset * SECONDS_IN_HOUR));

}

