Navigation:Home > Content >

FFCal_v03c.mq4

Time: 2015-09-14 | Download file:FFCal_v03c.mq4

//=============================================================================
//                                                            FFCal_v01.mq4
//                                            Copyright © 2006, Derk Wehler
// Written in cooperation with:                 http://www.forexfactory.com
//
//
// This "indicator" calls DLLs to fetch a special XML file from the 
// ForexFactory web site.  It then parses it and writes it out as a .CSV 
// file, which it places in the folder: experts/files so that IsNewsTime() 
// can use that file to tell if it is near announcement time.
//
// It does this once when it starts up and once per hour in case there have 
// been any updates to the annoucement calendar.
//
// In order for this to work correctly and synchronize with your broker's 
// server time, you must enter the proper value for "TimeZone".  This is 
// entered relative to GMT.  For example, my broker's server time is GMT+1, 
// so I have the default set to 1.  If your broker uses GMT+3, enter 3.
// If your broker uses GMT-2, then enter -2.
//
//
//
//=============================================================================
//                             OTHER CREDIT DUE
//      (For GrebWeb and LogUtils functionality (see end of this flie)
//
//  2/14/2007:  Robert Hill added code for using text objects instead
//              of Comment() for easier reading
//
//  2/25/2007:  Paul Hampton-Smith for his TimeZone DLL code
//
//  2/26/2007:  Mike Nguyen added the following things (search text for Added by MN):
//              -Connection test so that MT4 doesnt hang when there is no server connection                 
//              -Fixed some minor syntax because was getting "too many files open error..."
//              -Added vertical lines and vertical news text so that we have a  
//               visual reference of when the news happened.
//              -Now supports and correctly draws two simultaneous news announcements
//              -Added text to say "xx min SINCE event..." after the event as past
//              -Clean up old Objects left behind on old news annoumcents
//              -Now "Back Draws" Old news headlines onto the chart
//              -Now can choose which corner of the chart to place the headlines
//
//=============================================================================
//+---------------------------------------------------------------------------+
//|                                                               WinInet.mqh |
//|                                                        Paul Hampton-Smith |
//|                                                        paul1000@pobox.com |
//|                                                                           |
//| This include has been adapted from the groundbreaking GrabWeb script      |
//| developed by Adbi that showed me how to import functions from wininet.dll |
//|---------------------------------------------------------------------------|
//|                                                               grabweb.mq4 |
//|                                                    Copyright © 2006, Abhi |
//|                                         http://www.megadelfi.com/experts/ |
//|                                     E-mail: grabwebexpert{Q)megadelfi.com |
//|                                fix my e-mail address before mailing me ;) |
//+---------------------------------------------------------------------------+
/*
		IDEAS FOR ADDITIONS:
		
*   A text lable of what the news was and when it was, e.g
    "USD CPI m/m @ [broker time here]" at the top or bottom within the shaded area.
    
*   Label the number of pips it went in its initial direction and how much it 
	retraced...of course you'd have to come up with a convention for measuring 
	movement.  Or you could label what price was just prior to news then labels 
	the next two price exteremes.  I think it would be nice to have the numbers 
	right under/over the candle that produced the extremes for whatever timeframe 
	you are in.

*   Label the volume count for the candle that included the news in it...whatever 
	timeframe your in.  So I could switch from 1 min. to 5 min. to 15 min. 
	timeframes and get different vol. counts for the bar that included the news.
	
*/

#property copyright "Copyright © 2006, Derk Wehler"
#property link      "http://www.forexfactory.com"

#property indicator_chart_window
#property indicator_buffers 3

#define TITLE		0
#define COUNTRY		1
#define DATE		2
#define TIME		3
#define IMPACT		4
#define FORECAST	5
#define PREVIOUS	6

//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================

extern bool 	IncludeHigh 		= true;
extern bool 	IncludeMedium 		= true;
extern bool 	IncludeLow 			= false;
extern bool 	IncludeSpeaks 		= true; 	// news items with "Speaks" in them have different characteristics
extern int		Alert1MinsBefore	= -1;		// Set to -1 for no Alert
extern int		Alert2MinsBefore	= -1;		// Set to -1 for no Alert
extern bool 	EnableLogging 		= false; 	// Perhaps remove this from externs once its working well
extern bool		ShowNextTwoEvents	= true;
extern bool		ShowOldNews	= true;
extern int 		TxtSize 			= 10;
extern color 	TxtColorTitle 		= LightGray;
extern color 	TxtColorNews 		= DeepSkyBlue;
extern color 	TxtColorImpact 		= Red;
extern color 	TxtColorPrevious 	= Orange;
extern color 	TxtColorForecast 	= Lime;
extern int     VertTxtShift = 21;   //how far away below the ask line we want to place our vertical news text
extern int     VertLeftLineShift = 900;   //how far away to the left of the line we want to place our vertical news text
extern int     VertRightLineShift = 200;   //how far away to the left of the line we want to place our vertical news text
extern color 	VertLineColor = Yellow;   //color of our vertical news line
extern color 	VertTxtColor = Orange;   //color of our vertical text color 
extern int     VertTxtSize = 8;        //color of our vertical text
extern int     NewsCorner = 0;         //choose which corner to place headlines 0=Upper Left, 1=Upper Right, 2=lower left , 3=lower right

int		DebugLevel = 2;


double 	ExtMapBuffer0[];	// Contains (minutes until) each news event
double 	ExtMapBuffer1[];	// Contains only most recent and next news event ([0] & [1])
double 	ExtMapBuffer2[];	// Contains impact value for most recent and next news event


string	sUrl = "http://www.forexfactory.com/weekly_calendar.xml";
int 	xmlHandle;
int 	logHandle = -1;
int 	BoEvent, finalend, end, i;
int		begin;
string 	mainData[100][7];
int 	minsTillNews;
string 	sData, csvoutput;
string	commentStr;
int		tmpMins;
int		idxOfNext;
int		dispMinutes[2];
string 	dispTitle[2], 
		dispCountry[2], 
		dispImpact[2], 
		dispForecast[2], 
		dispPrevious[2];
string	dispSeparator1 = "===================";

static bool FirstRun = true;
static int	PrevMinute = -1;
datetime LastTimeAlert1=0;    //used to make sure we only draw something once per annoucement. Added by MN
  


int init()
{
	Print("In Init()...\n");
	
	SetIndexStyle(0, DRAW_NONE);
	SetIndexBuffer(0, ExtMapBuffer0);

	SetIndexStyle(1, DRAW_NONE);
	SetIndexBuffer(1, ExtMapBuffer1);

	IndicatorShortName("FFCal");
	SetIndexLabel(0, "MinsBeforeNews");
	SetIndexLabel(1, "MinsAfterNews"); 

	return(0);
}


int deinit()
{
	ObjectDelete("Sponsor"); 
	ObjectDelete("Minutes"); 
	ObjectDelete("Impact");
	ObjectDelete("Previous"); 
	ObjectDelete("Forecast");
	ObjectDelete("Separator1"); 
	ObjectDelete("Separator2");

	ObjectDelete("Sponsor2"); 
	ObjectDelete("Minutes2"); 
	ObjectDelete("Impact2");
	ObjectDelete("Previous2"); 
	ObjectDelete("Forecast2");
	
   ObjectsDeleteAll(0,OBJ_TEXT); //added by MN
   ObjectsDeleteAll(0,OBJ_TREND); //added by MN

	return(0);
}

int start()
{
	int 		newsIdx = 0;
	int 		nextNewsIdx = -1;
	int 		next;
	string 		myEvent;
	bool 		skip;
	datetime 	newsTime;
      
		
	//check to make sure we are connected, otherwise exit. Added by MN
	if(!IsConnected())
   {
      Print("News Indicator is disabled because NO CONNECTION to Broker!");
      return(0);
   }	
   
	// If we are not logging, then do not output debug statements either
	if (!EnableLogging)
		DebugLevel = 0;
				
	commentStr = "Forex Factory Calendar";
	string xmlFileName = Month() + "-" + Day() + "-" + Year() + "-" + "FFCal.xml";
	//Print("STARTED");
	
	// Refresh FF calendar on startup, and once at the top of each hour
	if (FirstRun || Minute() == 0)
	{
		FirstRun = false;

		if (DebugLevel > 1)
			Print("sUrl == ", sUrl);
		
		if (DebugLevel > 0)
			Print("Grabbing Web, url = ", sUrl);
	
		// THIS CALL WAS DONATED BY PAUL TO HELP FIX THE RESOURCE ERROR
		GrabWeb(sUrl, sData);

		if (DebugLevel > 0)
		{
			Print("Opening XML file...\n");
			Print(sData);
		}

		// Write the contents of the ForexFactory page to an .htm file
		xmlHandle = FileOpen(xmlFileName, FILE_BIN|FILE_READ|FILE_WRITE);
		if (xmlHandle < 1)
		{
			if (DebugLevel > 0)
				Print("Can\'t open new xml file, the last error is ", GetLastError());
			return(false);
		}
		FileWriteString(xmlHandle, sData, StringLen(sData));
		FileClose(xmlHandle);

		if (DebugLevel > 0)
			Print("Wrote XML file...\n");

		// THIS BLOCK OF CODE DONATED BY WALLY TO FIX THE RESOURCE ERROR
		//--- Look for the end XML tag to ensure that a complete page was downloaded ---//
		end = StringFind(sData, "", 0);

		if (end <= 0)
		{
			Alert("GetNewsFF Error - Web page download was not complete!");
			return(false);
		}
		//-------------------------------------------------------------------------------//
	}

	// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
	// Perform remaining checks once per minute
	if (Minute() == PrevMinute)
		return (true);
	PrevMinute = Minute();
	Print("GetNewsFF NEW MINUTE...Refreshing News from XML file...");

	// Open the log file (will not open if logging is turned off)
	OpenLog("FFCal");

	// Init the buffer array to zero just in case
	ArrayInitialize(ExtMapBuffer0, 0);
	ArrayInitialize(ExtMapBuffer1, 0);
	
	// Open the XML file
	xmlHandle = FileOpen(xmlFileName, FILE_BIN|FILE_READ);
	if (xmlHandle < 0)
	{
		Print("Can\'t open xml file: ", xmlFileName, ".  The last error is ", GetLastError());
		return(false);
	}
	if (DebugLevel > 0)
		Print("XML file open must be okay");
	
	// Read in the whole XML file
	// Ave file length == ~7K, so 65536 should always read whole file
	sData = FileReadString(xmlHandle, 65536);	
   	
	//Because MT4 build 202 complained about too many files open and MT4 hung. Added by MN
	if (xmlHandle > 0)
	{
   	FileClose(xmlHandle);
   }

	// Get the currency pair, and split it into the two countries
	string pair = Symbol();
	string cntry1 = StringSubstr(pair, 0, 3);
	string cntry2 = StringSubstr(pair, 3, 3);
	if (DebugLevel > 0)
		Print("cntry1 = ", cntry1, "    cntry2 = ", cntry2);
	
	if (DebugLevel > 0)
		Log("Weekly calendar for " + pair + "\n\n");

	// -------------------------------------------------
	// Parse the XML file looking for an event to report
	// -------------------------------------------------
	
	tmpMins = 10080;	// (a week)
	BoEvent = 0;

	string sTags[7] = { "", "<country>", "<date>", "<time>", "<impact>", "<forecast>", "<previous>" };
	string eTags[7] = { "", "", "", "", "", "", "" };
	while (true)
	{
		BoEvent = StringFind(sData, "", BoEvent);
		if (BoEvent == -1)
			break;
			
		BoEvent += 7;	
		next = StringFind(sData, "", BoEvent);
		myEvent = StringSubstr(sData, BoEvent, next - BoEvent);
		BoEvent = next;
		
		begin = 0;
		skip = false;
		for (i=0; i < 7; i++)
		{
			mainData[newsIdx][i] = "";
			next = StringFind(myEvent, sTags[i], begin);
			
			// Within this event, if tag not found, then it must be missing; skip it
			if (next == -1)
				continue;
			else
			{
				// We must have found the sTag okay...
				begin = next + StringLen(sTags[i]);			// Advance past the start tag
				end = StringFind(myEvent, eTags[i], begin);	// Find start of end tag
				if (end > begin && end != -1)
				{
					// Get data between start and end tag
					mainData[newsIdx][i] = StringSubstr(myEvent, begin, end - begin);
				}
			}
		}
		
//		for (i=6; i >= 0; i--)
//			Print(sTags[i], "  =  ", mainData[newsIdx][i]);

		// = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =
		// Test against filters that define whether we want to 
		// skip this particular annoucemnt
		if (cntry1 != mainData[newsIdx][COUNTRY] && cntry2 != mainData[newsIdx][COUNTRY])
			skip = true;
		if (!IncludeHigh && mainData[newsIdx][IMPACT] == "High") 
			skip = true;
		if (!IncludeMedium && mainData[newsIdx][IMPACT] == "Medium") 
			skip = true;
		if (!IncludeLow && mainData[newsIdx][IMPACT] == "Low") 
			skip = true;
		if (!IncludeSpeaks && (StringFind(mainData[newsIdx][TITLE], "speaks") != -1 || 
								StringFind(mainData[newsIdx][TITLE], "Speaks") != -1) ) 
			skip = true;
		if (mainData[newsIdx][TIME] == "All Day" || 
			mainData[newsIdx][TIME] == "Tentative" ||
			mainData[newsIdx][TIME] == "") 
      {		
			skip = true;
	   }		
		// = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =   = - =
		
		// If not skipping this event, then log it into the draw buffers
		if (!skip)
		{   
         // If we got this far then we need to calc the minutes until this event
			//
			// First, convert the announcement time to seconds, GMT
			newsTime = StrToTime(MakeDateTime(mainData[newsIdx][DATE], mainData[newsIdx][TIME]));
			
			// Now calculate the minutes until this announcement (may be negative)
			minsTillNews = (newsTime - TimeGMT()) / 60;
					
         //added by MN, if minsTillNews is zero, then its the time our news is hit.
         if ( (minsTillNews == 0) && (LastTimeAlert1 != newsTime) )
		   {  //draw the 1st news event onto the chart vertically so we have a visual record of when it occured
            DisplayVerticalNews(dispTitle[0], dispCountry[0], 0); 
            
            //if there is a 2nd simultaneous news event, display the 2nd news event
            if (dispMinutes[0] == dispMinutes[1])
            {
               DisplayVerticalNews(dispTitle[1], dispCountry[1], 1); 
            }
            //only draw once per announcement
            LastTimeAlert1 = newsTime;
         }

         //Back Draw old news onto the chart. Added by MN
         if (ShowOldNews)  
         {  //back draw 1st news headline
            DisplayOldNews(mainData[newsIdx][TITLE], mainData[newsIdx][COUNTRY], 0, StrToTime(MakeDateTime(mainData[newsIdx][DATE], mainData[newsIdx][TIME]))); 
            
            //if there is a 2nd simultaneously occuring news headline, draw that onto the chart as well
            if (mainData[newsIdx][TIME] == mainData[newsIdx+1][TIME])
            {
               DisplayOldNews(mainData[newsIdx+1][TITLE], mainData[newsIdx+1][COUNTRY], 1, StrToTime(MakeDateTime(mainData[newsIdx+1][DATE], mainData[newsIdx+1][TIME]))); 
            }
         }  

			// Keep track of the most recent news announcement.
			// Do that by saving each one until we get to the 
			// first annoucement that isn't in the past; i.e.
			// minsTillNews > 0.  Then, keep this one instead for
			// display, but only once the minutes until the next 
			// news is SMALLER than the minutes since the last.
//			Print("Mins till event: ", minsTillNews);
			if (minsTillNews < 0 || MathAbs(tmpMins) > minsTillNews)
			{
				idxOfNext = newsIdx;
				tmpMins	= minsTillNews;
			}
			
			Log("Weekly calendar for " + pair + "\n\n");
			if (DebugLevel > 0)
			{
				Log("FOREX FACTORY\nTitle: " + mainData[newsIdx][TITLE] + 
									"\nCountry: " + mainData[newsIdx][COUNTRY] + 
									"\nDate: " + mainData[newsIdx][DATE] + 
									"\nTime: " + mainData[newsIdx][TIME] + 
									"\nImpact: " + mainData[newsIdx][IMPACT] + 
									"\nForecast: " + mainData[newsIdx][FORECAST] + 
									"\nPrevious: " + mainData[newsIdx][PREVIOUS] + "\n\n");
			}
			
			// Do alert if user has enabled
			if (Alert1MinsBefore != -1 && minsTillNews == Alert1MinsBefore)
				Alert(Alert1MinsBefore, " minutes until news for ", pair, ": ", mainData[newsIdx][TITLE]);
			if (Alert2MinsBefore != -1 && minsTillNews == Alert2MinsBefore)
				Alert(Alert2MinsBefore, " minutes until news for ", pair, ": ", mainData[newsIdx][TITLE]);
				
			// Buffers are set up as so: 
			// ExtMapBuffer0 contains the time UNTIL each announcement (can be negative)
			// e.g. [0] = -372; [1] = 25; [2] = 450; [3] = 1768 (etc.)
			// ExtMapBuffer1[0] has the mintutes since the last annoucement.
			// ExtMapBuffer1[1] has the mintutes until the next annoucement.
			ExtMapBuffer0[newsIdx] = minsTillNews;
			newsIdx++;
		}
	}

	// Cycle through the events array and pick out the most recent 
	// past and the next coming event to put into ExtMapBuffer1. 
	// Put the corresponding impact for these two into ExtMapBuffer2.
	bool first = true;
	ExtMapBuffer1[0] = 0;
	ExtMapBuffer1[1] = 0;
	ExtMapBuffer2[0] = 0;
	ExtMapBuffer2[1] = 0;
	string outNews = "Minutes until news events for " + pair + " : ";
	for (i=0; i < newsIdx; i++)	
	{
		outNews = outNews + ExtMapBuffer0[i] + ", ";
		if (ExtMapBuffer0[i] > 0 && first)
		{
			first = false;
			
			// Put the relevant info into the indicator buffers...
			if (i > 0)
			{
				ExtMapBuffer1[0] = ExtMapBuffer0[i-1];
				ExtMapBuffer2[0] = ImpactToNumber(mainData[i-1][IMPACT]);
			}
			ExtMapBuffer1[1] = ExtMapBuffer0[i];
			ExtMapBuffer2[1] = ImpactToNumber(mainData[i][IMPACT]);
		}
		
		// Also use this loop to set which information to display
		if (i == idxOfNext)
		{
			dispTitle[0]	= mainData[i][TITLE];
			dispCountry[0] 	= mainData[i][COUNTRY];
			dispImpact[0] 	= mainData[i][IMPACT];
			dispForecast[0] = mainData[i][FORECAST];
			dispPrevious[0] = mainData[i][PREVIOUS];
			dispMinutes[0] 	= ExtMapBuffer0[i];
		}
		
		if (i == idxOfNext + 1)
		{
			dispTitle[1]	= mainData[i][TITLE];
			dispCountry[1] 	= mainData[i][COUNTRY];
			dispImpact[1] 	= mainData[i][IMPACT];
			dispForecast[1] = mainData[i][FORECAST];
			dispPrevious[1] = mainData[i][PREVIOUS];
			dispMinutes[1] 	= ExtMapBuffer0[i];
		}
		

	}	
	// If we are past all news events, then neither one will have been 
	// set, so set the past event to the last (negative) minutes
	if (ExtMapBuffer1[0] == 0 && ExtMapBuffer1[1] == 0)
   {
      ExtMapBuffer1[0] = ExtMapBuffer0[i-1];
    
      //ensures that we clean up so that we get old text left behind on old news annoumcents. Added by MN
   	ObjectDelete("Impact");
   	ObjectDelete("Previous"); 
   	ObjectDelete("Forecast");
   
   	ObjectDelete("Impact2");
	   ObjectDelete("Previous2"); 
   	ObjectDelete("Forecast2");
   }      

	
	// For debugging...Print the tines until news events, as a "Comment"
	if (DebugLevel > 0)
	{
		Print(outNews);
		Print("LastMins (ExtMapBuffer1[0]) = ", ExtMapBuffer1[0]);
		Print("NextMins (ExtMapBuffer1[1]) = ", ExtMapBuffer1[1]);
	}

    OutputToChart(dispMinutes[0]);

	if (logHandle > 0)
	{ FileClose(logHandle);   }
	
	return (0);
}

//Draws the news veritically on the Chart so that we have a visual reference of when it occurs. Added by MN 
void DisplayVerticalNews(string dispTitle, string dispCountry, int shift)
{    
   double 
      Pivot = (iHigh(NULL, 1440, 1) + iLow(NULL, 1440, 1) + iClose(NULL, 1440, 1)) / 3,  //Calculate our pivot point to determine where to place the news text
      Height = 0.0;
   
   //if Open price is above the Pivot determine our height
   if (Open[0] > Pivot)
   {
      Height = Low[iLowest(NULL,0,MODE_LOW,5,0)]-VertTxtShift*Point;  
   }           
   else //otherwise Open is below Pivot
   {
      Height = High[iHighest(NULL,0,MODE_HIGH,5,0)]+VertTxtShift*Point;          
   }
   
   if (shift == 0)
   {
      //draw a vertical line at the time of the news if it hasnt already been drawn
      if(ObjectFind("vLine"+Time[0]) == -1)
      {
         ObjectCreate("vLine"+Time[0], OBJ_TREND, 0, Time[0], 0, Time[0], High[0]); //experimental
         ObjectSet("vLine"+Time[0], OBJPROP_COLOR, VertLineColor);
         ObjectSet("vLine"+Time[0], OBJPROP_STYLE, STYLE_DOT);				   
         ObjectSet("vLine"+Time[0], OBJPROP_BACK, true);                                //put object in the background behind any other object on the chart
      }
         
      //place our news if it hasnt already been placed on our chart
      if(ObjectFind("Headline"+Time[0]) == -1)
      {  //x value use Time[0], for y value find the lowest bar within the last 10 bars, subtract by VertTxtShift and used that as our y coordinate
         ObjectCreate("Headline"+Time[0], OBJ_TEXT, 0, Time[0]-VertLeftLineShift, Height); 
         ObjectSet("Headline"+Time[0], OBJPROP_ANGLE, 90);                               //rotate the text 90 degrees
         ObjectSetText("Headline"+Time[0], "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor);
      }
   }
   else //draw second news headline
   {
      //place our news if it hasnt already been placed on our chart
      if(ObjectFind("Headline"+Time[0]+"s") == -1)
      {  //x value use Time[0], for y value find the lowest bar within the last 10 bars, subtract by VertTxtShift and used that as our y coordinate
         ObjectCreate("Headline"+Time[0]+"s", OBJ_TEXT, 0, Time[0]+VertRightLineShift, Height); 
         ObjectSet("Headline"+Time[0]+"s", OBJPROP_ANGLE, 90);                               //rotate the text 90 degrees
         ObjectSetText("Headline"+Time[0]+"s", "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor);
      }
   }
         
   //force a redraw of our chart
   WindowRedraw();
   
   return(0);
}

//Back Draw Old news veritically on the Chart so that we have a visual reference of when it did occur. Added by MN 
void DisplayOldNews(string dispTitle, string dispCountry, int shift, datetime TheTime)
{     
   int   
      BarShift = iBarShift(NULL, 0, TheTime);   //we have TheTime, now we need the shift so we can access this Bar's High/Low/Close information
   
   double 
      Pivot = (iHigh(NULL, 1440, BarShift) + iLow(NULL, 1440, BarShift) + iClose(NULL, 1440, BarShift)) / 3,  //Calculate our pivot point to determine where to place the news text
      Height = 0.0;

   //if open price is above the Pivot, determine our height
   if (Open[BarShift] > Pivot)
   {
      Height = Low[iLowest(NULL,0,MODE_LOW,5,BarShift)]-VertTxtShift*Point;
   }
   else //otherwise Open is below Pivot
   {
      Height = High[iHighest(NULL,0,MODE_HIGH,5,BarShift)]+VertTxtShift*Point;
   }

   //Print("Title recieved : " + dispTitle + " Country Recieved: " + dispCountry + " Date and time Recieved: " + TheTime);
 
   //we only want to vertically draw the news if it hasnt occured yet
   if (TheTime < Time[0])
   {  //draw the first news headline 
      if (shift == 0)
      {
         //draw a vertical line at the time of the news if it hasnt already been drawn
         if(ObjectFind("vLine"+TheTime) == -1)
         {
            ObjectCreate("vLine"+TheTime, OBJ_TREND, 0, TheTime, 0, TheTime, High[0]); //experimental
            ObjectSet("vLine"+TheTime, OBJPROP_COLOR, VertLineColor);
            ObjectSet("vLine"+TheTime, OBJPROP_STYLE, STYLE_DOT);				   
            ObjectSet("vLine"+TheTime, OBJPROP_BACK, true);                                //put object in the background behind any other object on the chart
         }
                  
         //place our news if it hasnt already been placed on our chart
         if(ObjectFind("Headline"+TheTime) == -1)
         {  //x value use Time[0], for y value find the lowest bar within the last 10 bars, subtract by VertTxtShift and used that as our y coordinate
            ObjectCreate("Headline"+TheTime, OBJ_TEXT, 0, TheTime-VertLeftLineShift, Height); 
            ObjectSet("Headline"+TheTime, OBJPROP_ANGLE, 90);                               //rotate the text 90 degrees
            ObjectSetText("Headline"+TheTime, "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor);
         }            
      }
      else //draw second news headline
      {
         //place our news if it hasnt already been placed on our chart
         if(ObjectFind("Headline"+TheTime+"s") == -1)
         {  //x value use Time[0], for y value find the lowest bar within the last 10 bars, subtract by VertTxtShift and used that as our y coordinate
            ObjectCreate("Headline"+TheTime+"s", OBJ_TEXT, 0, TheTime+VertRightLineShift, Height); 
            ObjectSet("Headline"+TheTime+"s", OBJPROP_ANGLE, 90);                               //rotate the text 90 degrees
            ObjectSetText("Headline"+TheTime+"s", "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor);
         }   
      }//end of "if shift)
   }//end of (TheTime < Time[0])
        
   //force a redraw of our chart
   WindowRedraw();
   
   return(0);
}

void OutputToChart(int dispMin)
{
	// Added by Robert for using TxtSize and TxtColor for easier reading
	int curY = 10;
	ObjectCreate("Separator1", OBJ_LABEL, 0, 0, 0);
	ObjectSetText("Separator1", dispSeparator1, TxtSize, "Arial Bold", TxtColorTitle);
	ObjectSet("Separator1", OBJPROP_CORNER, NewsCorner);
	ObjectSet("Separator1", OBJPROP_XDISTANCE, 10);
	ObjectSet("Separator1", OBJPROP_YDISTANCE, curY); 

	curY = curY + TxtSize + 4;
	ObjectCreate("Sponsor", OBJ_LABEL, 0, 0, 0);
	ObjectSetText("Sponsor", commentStr, TxtSize, "Arial Bold", TxtColorTitle);
	ObjectSet("Sponsor", OBJPROP_CORNER, NewsCorner);
	ObjectSet("Sponsor", OBJPROP_XDISTANCE, 10);
	ObjectSet("Sponsor", OBJPROP_YDISTANCE, curY);       

	curY = curY + TxtSize + 4;
	ObjectCreate("Separator2", OBJ_LABEL, 0, 0, 0);
	ObjectSetText("Separator2", dispSeparator1, TxtSize, "Arial Bold", TxtColorTitle);
	ObjectSet("Separator2", OBJPROP_CORNER, NewsCorner);
	ObjectSet("Separator2", OBJPROP_XDISTANCE, 10);
	ObjectSet("Separator2", OBJPROP_YDISTANCE, curY);   

//	curY = curY + TxtSize + 4;
//	ObjectCreate("Minutes", OBJ_LABEL, 0, 0, 0);
//	ObjectSetText("Minutes", dispMinutes[0] + " Mins until " + dispCountry[0] + ": " + dispTitle[0], TxtSize, "Arial Bold", TxtColorNews);
//	ObjectSet("Minutes", OBJPROP_CORNER, NewsCorner);
//	ObjectSet("Minutes", OBJPROP_XDISTANCE, 10);
//	ObjectSet("Minutes", OBJPROP_YDISTANCE, curY);

   //if the time is negative, we want to say xxx mins SINCE .... news event
   if (dispMin < 0)
   {
	curY = curY + TxtSize + 4;
      ObjectCreate("Minutes", OBJ_LABEL, 0, 0, 0);
      ObjectSetText("Minutes",dispMinutes[0] + " mins" + " since news of " + dispCountry[0] + ": " + dispTitle[0], TxtSize, "Arial Bold", TxtColorNews);
      ObjectSet("Minutes", OBJPROP_CORNER, NewsCorner);
      ObjectSet("Minutes", OBJPROP_XDISTANCE, 10);
      ObjectSet("Minutes", OBJPROP_YDISTANCE, curY);
   }
   else
   {  //otherwise, we say xxx until .... news event  
	curY = curY + TxtSize + 4;
      ObjectCreate("Minutes", OBJ_LABEL, 0, 0, 0);
      ObjectSetText("Minutes",dispMinutes[0] + " mins" + " until " + dispCountry[0] + ": " + dispTitle[0], TxtSize, "Arial Bold", TxtColorNews);
      ObjectSet("Minutes", OBJPROP_CORNER, NewsCorner);
      ObjectSet("Minutes", OBJPROP_XDISTANCE, 10);
      ObjectSet("Minutes", OBJPROP_YDISTANCE, curY);
   }   

	curY = curY + TxtSize + 4;
	ObjectCreate("Impact", OBJ_LABEL, 0, 0, 0);
	ObjectSetText("Impact", "Impact: " + dispImpact[0], TxtSize, "Arial Bold", TxtColorImpact);
	ObjectSet("Impact", OBJPROP_CORNER, NewsCorner);
	ObjectSet("Impact", OBJPROP_XDISTANCE, 10);
	ObjectSet("Impact", OBJPROP_YDISTANCE, curY);

	if (dispPrevious[0] != "")
	{
		curY = curY + TxtSize + 4;
		ObjectCreate("Previous", OBJ_LABEL, 0, 0, 0);
		ObjectSetText("Previous", "Previous: " + dispPrevious[0], TxtSize, "Arial Bold", TxtColorPrevious);
		ObjectSet("Previous", OBJPROP_CORNER, NewsCorner);
		ObjectSet("Previous", OBJPROP_XDISTANCE, 10);
		ObjectSet("Previous", OBJPROP_YDISTANCE, curY);
	}
	
	if (dispForecast[0] != "")
	{
		curY = curY + TxtSize + 4;
		ObjectCreate("Forecast", OBJ_LABEL, 0, 0, 0);
		ObjectSetText("Forecast", "Forecast: " + dispForecast[0], TxtSize, "Arial Bold", TxtColorForecast);
		ObjectSet("Forecast", OBJPROP_CORNER, NewsCorner);
		ObjectSet("Forecast", OBJPROP_XDISTANCE, 10);
		ObjectSet("Forecast", OBJPROP_YDISTANCE, curY); 
	}
	
	// Do second news event
	if (ShowNextTwoEvents)
	{
		curY = curY + TxtSize + 20;
		ObjectCreate("Minutes2", OBJ_LABEL, 0, 0, 0);
		ObjectSetText("Minutes2", dispMinutes[1] + " mins until " + dispCountry[1] + ": " + dispTitle[1], TxtSize, "Arial Bold", TxtColorNews);
		ObjectSet("Minutes2", OBJPROP_CORNER, NewsCorner);
		ObjectSet("Minutes2", OBJPROP_XDISTANCE, 10);
		ObjectSet("Minutes2", OBJPROP_YDISTANCE, curY);

		curY = curY + TxtSize + 4;
		ObjectCreate("Impact2", OBJ_LABEL, 0, 0, 0);
		ObjectSetText("Impact2", "Impact: " + dispImpact[1], TxtSize, "Arial Bold", TxtColorImpact);
		ObjectSet("Impact2", OBJPROP_CORNER, NewsCorner);
		ObjectSet("Impact2", OBJPROP_XDISTANCE, 10);
		ObjectSet("Impact2", OBJPROP_YDISTANCE, curY);

		if (dispPrevious[1] != "")
		{
			curY = curY + TxtSize + 4;
			ObjectCreate("Previous2", OBJ_LABEL, 0, 0, 0);
			ObjectSetText("Previous2", "Previous: " + dispPrevious[1], TxtSize, "Arial Bold", TxtColorPrevious);
			ObjectSet("Previous2", OBJPROP_CORNER, NewsCorner);
			ObjectSet("Previous2", OBJPROP_XDISTANCE, 10);
			ObjectSet("Previous2", OBJPROP_YDISTANCE, curY);
		}
		
		if (dispForecast[1] != "")
		{
			curY = curY + TxtSize + 4;
			ObjectCreate("Forecast2", OBJ_LABEL, 0, 0, 0);
			ObjectSetText("Forecast2", "Forecast: " + dispForecast[1], TxtSize, "Arial Bold", TxtColorForecast);
			ObjectSet("Forecast2", OBJPROP_CORNER, NewsCorner);
			ObjectSet("Forecast2", OBJPROP_XDISTANCE, 10);
			ObjectSet("Forecast2", OBJPROP_YDISTANCE, curY); 
		}
	}
	
	return (0);
}


int ImpactToNumber(string impact)
{
	if (impact == "High")
		return (3);
	if (impact == "Medium")
		return (2);
	if (impact == "Low")
		return (1);
	else
		return (0);
}
   
string MakeDateTime(string strDate, string strTime)
{
   // Print("Converting Forex Factory Time into Metatrader time..."); //added by MN
	// converts forexfactory time & date into yyyy.mm.dd hh:mm
	int n1stDash = StringFind(strDate, "-");
	int n2ndDash = StringFind(strDate, "-", n1stDash+1);

	string strMonth = StringSubstr(strDate, 0, 2);
	string strDay = StringSubstr(strDate, 3, 2);
	string strYear = StringSubstr(strDate, 6, 2); 
	strYear = "20" + strYear;
	
	int nTimeColonPos	= StringFind(strTime, ":");
	string strHour = StringSubstr(strTime, 0, nTimeColonPos);
	string strMinute = StringSubstr(strTime, nTimeColonPos+1, 2);
	string strAM_PM = StringSubstr(strTime, StringLen(strTime)-2);

	int nHour24 = StrToInteger(strHour);
	if (strAM_PM == "pm" || strAM_PM == "PM" && nHour24 != 12)
	{
		nHour24 += 12;
	}
	string strHourPad = "";
	if (nHour24 < 10) 
		strHourPad = "0";

	return(StringConcatenate(strYear, ".", strMonth, ".", strDay, " ", strHourPad, nHour24, ":", strMinute));
}


//=================================================================================================
//=================================================================================================
//====================================   GrabWeb Functions   ======================================
//=================================================================================================
//=================================================================================================
// Main Webscraping function
// ~~~~~~~~~~~~~~~~~~~~~~~~~
// bool GrabWeb(string strUrl, string& strWebPage)
// returns the text of any webpage. Returns false on timeout or other error
// 
// Parsing functions
// ~~~~~~~~~~~~~~~~~
// string GetData(string strWebPage, int nStart, string strLeftTag, string strRightTag, int& nPos)
// obtains the text between two tags found after nStart, and sets nPos to the end of the second tag
//
// void Goto(string strWebPage, int nStart, string strTag, int& nPos)
// Sets nPos to the end of the first tag found after nStart 

bool bWinInetDebug = false;

int hSession_IEType;
int hSession_Direct;
int Internet_Open_Type_Preconfig = 0;
int Internet_Open_Type_Direct = 1;
int Internet_Open_Type_Proxy = 3;
int Buffer_LEN = 13;

#import "wininet.dll"

#define INTERNET_FLAG_PRAGMA_NOCACHE    0x00000100 // Forces the request to be resolved by the origin server, even if a cached copy exists on the proxy.
#define INTERNET_FLAG_NO_CACHE_WRITE    0x04000000 // Does not add the returned entity to the cache. 
#define INTERNET_FLAG_RELOAD            0x80000000 // Forces a download of the requested file, object, or directory listing from the origin server, not from the cache.

int InternetOpenA(
	string 	sAgent,
	int		lAccessType,
	string 	sProxyName="",
	string 	sProxyBypass="",
	int 	lFlags=0
);

int InternetOpenUrlA(
	int 	hInternetSession,
	string 	sUrl, 
	string 	sHeaders="",
	int 	lHeadersLength=0,
	int 	lFlags=0,
	int 	lContext=0 
);

int InternetReadFile(
	int 	hFile,
	string 	sBuffer,
	int 	lNumBytesToRead,
	int& 	lNumberOfBytesRead[]
);

int InternetCloseHandle(
	int 	hInet
);
#import


int hSession(bool Direct)
{
	string InternetAgent;
	if (hSession_IEType == 0)
	{
		InternetAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)";
		hSession_IEType = InternetOpenA(InternetAgent, Internet_Open_Type_Preconfig, "0", "0", 0);
		hSession_Direct = InternetOpenA(InternetAgent, Internet_Open_Type_Direct, "0", "0", 0);
	}
	if (Direct) 
	{ 
		return(hSession_Direct); 
	}
	else 
	{
		return(hSession_IEType); 
	}
}


bool GrabWeb(string strUrl, string& strWebPage)
{
	int 	hInternet;
	int		iResult;
	int 	lReturn[]={1};
	string 	sBuffer="x";
	int 	bytes;
	
	hInternet = InternetOpenUrlA(hSession(FALSE), strUrl, "0", 0, 
								INTERNET_FLAG_NO_CACHE_WRITE | 
								INTERNET_FLAG_PRAGMA_NOCACHE | 
								INTERNET_FLAG_RELOAD, 0);
								
	if (bWinInetDebug) 
		Log("hInternet: " + hInternet);   
	if (hInternet == 0) 
		return(false);

   Print("Reading URL: " + strUrl);	   //added by MN	
	iResult = InternetReadFile(hInternet, sBuffer, Buffer_LEN, lReturn);
	
	if (bWinInetDebug) 
		Log("iResult: " + iResult);
	if (bWinInetDebug) 
		Log("lReturn: " + lReturn[0]);
	if (bWinInetDebug) 
		Log("iResult: " + iResult);
	if (bWinInetDebug) 
		Log("sBuffer: " +  sBuffer);
	if (iResult == 0) 
		return(false);
	bytes = lReturn[0];

	strWebPage = StringSubstr(sBuffer, 0, lReturn[0]);
	
	// If there's more data then keep reading it into the buffer
	while (lReturn[0] != 0)
	{
		iResult = InternetReadFile(hInternet, sBuffer, Buffer_LEN, lReturn);
		if (lReturn[0]==0) 
			break;
		bytes = bytes + lReturn[0];
		strWebPage = strWebPage + StringSubstr(sBuffer, 0, lReturn[0]);
	}

   Print("Closing URL web connection");   //added by MN
	iResult = InternetCloseHandle(hInternet);
	if (iResult == 0) 
		return(false);
		
/*	if (bWinInetDebug) 
	{ 	
		Log("iResult: " + iResult);
		int handle = FileOpen("Grabweb.htm",FILE_BIN|FILE_READ|FILE_WRITE);
		FileWriteString(handle,strWebPage,StringLen(strWebPage));
		FileClose(handle);
	} */
	return(true);
}


/*
string GetData(string strWebPage, int nStart, string strLeftTag, string strRightTag, int& nPos)
{
	nPos = StringFind(strWebPage,strLeftTag,nStart);
	if (nPos == -1)
	{
		if (bWinInetDebug) Log("GetData(WebPage,"+nStart+","+strLeftTag+","+strRightTag+"): not found, returning empty string and -1");
		return("");
	}
	nPos += StringLen(strLeftTag);
	int nEnd = StringFind(strWebPage,strRightTag,nPos+1);
	string strData = StringTrimLeft(StringTrimRight(StringSubstr(strWebPage,nPos,nEnd-nPos)));
	if (bWinInetDebug) Log("GetData(WebPage,"+nStart+","+strLeftTag+","+strRightTag+") returning "+strData+" and "+nPos);
	return(strData);
}

void Goto(string strWebPage, int nStart, string strTag, int& nPos)
{
	nPos = StringFind(strWebPage,strTag,nStart);
	if (nPos == -1) 
	{
		if (bWinInetDebug) Log("Goto("+nStart+","+strTag+") not found. Returning -1");
		return;
	}
	nPos += StringLen(strTag);
	if (bWinInetDebug) Log("Goto("+nStart+","+strTag+") returning "+nPos);
}

void SaveWebPage(string strWebPage, string strID)
{
	string strFilename = strID+"_"+Year()+Pad(Month())+Pad(Day())+"-"+Pad(Hour())+Pad(Minute())+Pad(Seconds())+".htm";
	int handle = FileOpen(strFilename,FILE_BIN|FILE_WRITE);
	FileWriteString(handle,strWebPage,StringLen(strWebPage));
   FileClose(handle);
}


string Pad(int n)
{
	string strPad = "";
	if (n <= 9) strPad = "0";
	return(strPad+n);
}
*/

//=================================================================================================
//=================================================================================================
//===================================   LogUtils Functions   ======================================
//=================================================================================================
//=================================================================================================

void OpenLog(string strName)
{
	if (!EnableLogging) 
		return;

	if (logHandle <= 0)
	{
		string strMonthPad = "";
 	 	string strDayPad = "";
  		if (Month() < 10) 
  			strMonthPad = "0";
  		if (Day() < 10) 
  			strDayPad = "0";
  			
  		string strFilename = StringConcatenate(strName, "_", Year(), strMonthPad, Month(), strDayPad, Day(), "_log.txt");
  		
		logHandle = FileOpen(strFilename,FILE_CSV|FILE_READ|FILE_WRITE);
		Print("logHandle =================================== ", logHandle);
	}
	if (logHandle > 0)
	{
		FileFlush(logHandle);
		FileSeek(logHandle, 0, SEEK_END);
	}
}


void Log(string msg)
{
	if (!EnableLogging) 
		return;
		
	if (logHandle <= 0) 
		return;
		
	msg = TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES|TIME_SECONDS) + " " + msg;
	FileWrite(logHandle,msg);
}

//=================================================================================================
//=================================================================================================
//===================================   Timezone Functions   ======================================
//=================================================================================================
//=================================================================================================


#import "kernel32.dll"
int  GetTimeZoneInformation(int& TZInfoArray[]);
#import

#define TIME_ZONE_ID_UNKNOWN   0
#define TIME_ZONE_ID_STANDARD  1
#define TIME_ZONE_ID_DAYLIGHT  2

// Local timezone in hours, adjusting for daylight saving
double TimeZoneLocal()
{
	int TZInfoArray[43];

	switch(GetTimeZoneInformation(TZInfoArray))
	{
	case TIME_ZONE_ID_UNKNOWN: 
		Print("Error obtaining PC timezone from GetTimeZoneInformation in kernel32.dll. Returning 0");
		return(0);

	case TIME_ZONE_ID_STANDARD:
		return(TZInfoArray[0]/(-60.0));
	
	case TIME_ZONE_ID_DAYLIGHT:
		return((TZInfoArray[0]+TZInfoArray[42])/(-60.0));
		
	default:
		Print("Unkown return value from GetTimeZoneInformation in kernel32.dll. Returning 0");
		return(0);
	}
}

// Server timezone in hours
double TimeZoneServer()
{
	int ServerToLocalDiffMinutes = (TimeCurrent()-TimeLocal())/60;
	
	// round to nearest 30 minutes to allow for inaccurate PC clock
	int nHalfHourDiff = MathRound(ServerToLocalDiffMinutes/30.0);
	ServerToLocalDiffMinutes = nHalfHourDiff*30;
	return(TimeZoneLocal() + ServerToLocalDiffMinutes/60.0);
}

// Uses local PC time, local PC timezone, and server time to calculate GMT time at arrival of last tick
datetime TimeGMT()
{
	// two ways of calculating
	// 1. From PC time, which may not be accurate
	// 2. From server time. Most accurate except when server is down on weekend
	datetime dtGmtFromLocal = TimeLocal() - TimeZoneLocal()*3600;
	datetime dtGmtFromServer = TimeCurrent() - TimeZoneServer()*3600;

	// return local-derived value if server value is out by more than 5 minutes, eg during weekend
	if (dtGmtFromLocal > dtGmtFromServer + 300)
	{
		return(dtGmtFromLocal);
	}
	else
	{
		return(dtGmtFromServer);
	}	
}

//=================================================================================================
//=================================================================================================
//=================================   END IMPORTED FUNCTIONS  =====================================
//=================================================================================================
//=================================================================================================

Recommend