Navigation´╝ÜHome > Content >

#3_FFCal_Panel_(White).mq4

Time: 2012-02-21 | Download file:#3_FFCal_Panel_(White).mq4

//------------------------------------------------------------------------------------------------+
//                                                                                                |
//                                        FFCal Panel.mq4                                         |
//                                                                                                |
//------------------------------------------------------------------------------------------------+
#property copyright "Copyright @ 2015 traderathome, atstrader, deVries, qFish"
#property link      "email: traderathome@msn.com"
//#property strict
/*-------------------------------------------------------------------------------------------------
User Notes:

This FFCal Panel indicator is coded to run on MT4 Build 600+.  It places a panel on the chart in 
which are displayed up to four news releases listed on the Forex Factory Calendar.  Details are
provided below. 

FFCAL PANEL CONTENTS -
The time, title and ranking (by color) of the releases are shown.

The time of a releases is important because the market frequently holds price moves until after
important releases come out.  Also, Market Makers can prepare in advance for these moves.  The
Market Makers and banks do know the essence of news and releases in advance, and there is no
regulation against insider trading in the forex market.  Banks can place orders in advance of
releases and Market Makers can use the intervening time to move prices to fill those orders,
saving the move that will make profits on the orders until the time of the releases.

The title of the release, and it's ranking by color, is important because the variety of low
impact releases frequently have no effect on price, whereas the variety of high impact releases
can trigger a big price move, or can cause Market Makers to start moving prices days in advance,
in preparations for big moves to be made in conjunction with those releases.

Of the three impact level events (High, Medium, Low) and Bank Holidays, you have the option to not
show all but the High Impact events.  The Previous/Forecast data (available on the Forex Factory
Calendar) is not displayed because prices can go either way regardless of specifics released.  it
is the timing of news that is important, as a market volatility event.

THE PRIORITIZATION OF EVENTS -
Prioritization of events is fully automated and two more event labels are added to help avoid
surprises.  When two or more events occur simultaneously, only one event of the highest impact
displays unless there are multiple high impact events.  In that case the multple high impact
events are displayed.  When 2nd, 3rd or 4th events are not scheduled, text noting that appears.
A current day Bank Holiday will remain displayed in the first label until the Bank Holiday is
over.  You can show events for any pair on any chart.  For example, you can show  a CNY (China)
event on a AUDUSD chart.  A new option is added allowing you to ignore the chart pair and
select what currencies you want to show news for, so that you do not have to display the news
for the chart pair.

THE ALERT -
You can specify to be alerted "x" minutes ahead of the scheduled release of Event #1.  When
that time arrives, an alert will sound/appear on the chart.  If during the same minute, while
the relase time and the alert time are of the same minute, if you change chart time framse the
alert will retrigger on the selected new chart time frame.  After that minute passes, there will
be no retrigger of the set alert under any circumstances.  Enter your "Broker_Name_In_Alert" to
avoid confusion if simultaneously using multiple platforms.

When "Alert_Minutes_Before_Event_1" is set at "0" the Alert is not set to function.   If any
other number appears, the Alert is set to function.  When the Alert is set to function....
   * If the minutes input is less than the time to the release of Event #1, the Alert is set
     to trigger and "Alert Pending" appears in the FFCal title.
   * If the minutes input equals the time to the release, the Alert triggers and "Alert Received"
     appears in the FFCal title.
   * If the minutes input now exceeds the time to the release, the release is already one or
     more minutes into the past and "Alert Completed" appears in the FFCal title.  When this is
     the case, open the External Inputs and reset the input to "0" to turn off the Alert.

THE TFs FOR DISPLAY OF FFCAL -
You can select a range of TFs for the display of this indicator, so it automatically will not
display on a chart TF outside that range.  The indicator can be turned on/off without having
to remove it from the chart, thereby preserving your chart settings.

                                                                        - Traderathome, 05-30-2015
---------------------------------------------------------------------------------------------------
ACKNOWLEDGEMENTS:

derkwehler and other contributors - the core code of the FFCal indicator,
                                    FFCal_v20 dated 07/07/2009, Copyright @ 2006 derkwehler
                                    http://www.forexfactory.com/showthread.php?t=19293
                                    email: derkwehler@gmail.com

deVries -      for his excellent donated work that significantly altered and streamlined the file
               handling coding to establish compatibility with the new release of MT4 Build 600+,
               (Jobs for deVries  www.mql5.com/en/job/new?prefered=deVries)
qFish -        for his generously given time and help during the effort to improve this indicator.
atstrader -    For the option controlling for what pair/pairs(s) news is shown, and for providing
               new file access coding in 2015.
traderathome - for the modification of the original work to show more and prioritized headlines.

---------------------------------------------------------------------------------------------------
Suggested Colors:                    White Charts          Black Charts

FFCal_Title                          Black                 C'180,180,180'
News_Low_Impact                      C'000,125,029'        C'046,186,046'
News_Medium_Impact                   MediumBlue            C'098,147,238'
News_High_Impact                     Crimson               C'240,038,038'
Bank_Holiday_Color                   DarkOrchid            Orchid
Remarks_Color                        DarkGray              DimGray
Background_Color_                    White                 C'010,010,010''

-------------------------------------------------------------------------------------------------*/


//+-----------------------------------------------------------------------------------------------+
//| Indicator Global Inputs                                                                       |
//+-----------------------------------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1  CLR_NONE

#define READURL_BUFFER_SIZE   100

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

#import  "Wininet.dll"
   int InternetOpenW(string, int, string, string, int);
   int InternetConnectW(int, string, int, string, string, int, int, int);
   int HttpOpenRequestW(int, string, string, int, string, int, string, int);
//start 2015 atstrader revision
   //   int InternetOpenUrlW(int, string, string, int, int, int);
//end 2015 atstrader revision
   int InternetOpenUrlW(int, string, string, uint, uint, uint);
   int InternetReadFile(int, uchar & arr[], int, int & arr[]);
   int InternetCloseHandle(int);
#import

//global external inputs---------------------------------------------------------------------------
extern string Part_1                          = "Indicator Display Controls:";
extern bool   Indicator_On                    = true;
extern int    Show_H_HM_HML_123               = 3;
extern bool   Allow_Panel_In_Subwindow_1      = true;
extern bool   Allow_Panel_At_Chart_Right      = false;
extern int    Display_Min_TF                  = 1;
extern int    Display_Max_TF                  = 43200;
extern string TF_Choices                      = "1-5-15-30-60-240-1440-10080-43200";

extern string __                              = "";
extern string Part_2                          = "Headline Settings:";
extern color  FFCal_Title 	                   = Black;
extern color  Low_Impact_News_Color           = C'000,125,029';
extern color  Medium_Impact_News_Color        = MediumBlue;
extern color  High_Impact_News_Color          = Crimson;
extern color  Bank_Holiday_Color              = DarkOrchid;
extern color  Remarks_Color                   = DarkGray;
extern color  Background_Color_               = White;
extern bool   Show_Panel_Background           = true;

extern string ___                             = "";
extern string Part_3                          = "Other Currency Settings:";
extern bool	  Show_USD_News                   = false;
extern bool	  Show_EUR_News                   = false;
extern bool	  Show_GBP_News                   = false;
extern bool	  Show_NZD_News                   = false;
extern bool	  Show_JPY_News                   = false;
extern bool	  Show_AUD_News                   = false;
extern bool	  Show_CAD_News                   = false;
extern bool	  Show_CHF_News                   = false;
extern bool	  Show_CNY_News                   = false;
extern bool	  Ignore_Current_Symbol           = false;

extern string ____                            = "";
extern string Part_4                          = "Alert Setting:";
extern int	  Alert_Minutes_Before_Event_1    = 0;

//global buffers and variables---------------------------------------------------------------------
bool          Deinitialized, skip;
bool          FLAG_1_done, FLAG_2_done, FLAG_3_done, FLAG_4_done;
bool          FLAG_1_none, FLAG_2_none, FLAG_3_none, FLAG_4_none;
int           BankIdx1,cnr;
color         TxtColorNews;
datetime      newsTime,calendardate;   //sundaydate calendar
double   	  ExtMapBuffer0[];     // Contains (minutes until) each news event
int 	        xmlHandle, BoEvent, finalend, end, begin, minsTillNews, tmpMins, idxOfNext;
int           Minutes[8], newsIdx, next, dispMins, Days, Hours, Mins;
int           i,k,curX,curY,W,Box,x1,x2,index;
int           WebUpdateFreq = 240; // 240 Minutes between web updates to not overload FF server
int           TxtSize       = 7;
int           TitleSpacer   = 6; 
int           EventSpacer   = 4; 
static int	  PrevMinute    = -1;
string 		  xmlFileName;
string        xmlSearchName;
int           PrevTF        = 0;
static int	  UpdateRateSec = 10;
string	     sUrl = "http://www.forexfactory.com/ff_calendar_thisweek.xml"; //original
//string      sUrl = "http://www.forexfactory.com/ffcal_week_this.xml";      //alternate 1
//string      sUrl = "http://cdn.forexfactory.com/ffcal_week_this.xml";      //alternate 2
string   	  myEvent,mainData[200][7], sData, csvoutput, sinceUntil, TimeStr;
string    	  G,/*pair, cntry1, cntry2,*/ Title[8], Country[8], Impact[8],event[5],Color[5];
string 	     sTags[7] = { "", "<country>", "<date><![CDATA[","<time><![CDATA[",
              "<impact><![CDATA[", "<forecast><![CDATA[", "<previous><![CDATA[" };
string 	     eTags[7] = { "", "", "]]>", "]]>",
              "]]>", "]]>", "]]>" };
string        Text_Style = "Courier Narrow";
string        header;
string        box1    = "z[FFCal] Box1";
string        box2    = "z[FFCal] Box2";
string        News1   = "z[FFCal] News1";
string        News2   = "z[FFCal] News2";
string        News3   = "z[FFCal] News3";
string        News4   = "z[FFCal] News4";
string        Sponsor = "z[FFCal] Sponsor";

//Alert
bool          Alert_Allowed;

//+-----------------------------------------------------------------------------------------------+
//| Indicator De-initialization                                                                   |
//+-----------------------------------------------------------------------------------------------+
int deinit()
   {
   int obj_total= ObjectsTotal();
   for (i= obj_total; i>=0; i--) 
      {
      string name= ObjectName(i);
      if (StringSubstr(name,0,8)=="z[FFCal]") {ObjectDelete(name);}
      }
	return(0);
   }

//+-----------------------------------------------------------------------------------------------+
//| Indicator Initialization                                                                      |
//+-----------------------------------------------------------------------------------------------+
int init()
   {
   //Get current time frame
   PrevTF = Period();
	//Make sure we are connected.  Otherwise exit.
   //With the first DLL call below, the program will exit (and stop) automatically after one alert.
   if ( !IsDllsAllowed() ) 
      {
      Alert(Symbol()," ",Period(),", FFCal: Allow DLL Imports");
      }

	//deVries: Management of FFCal.xml Files involves setting up a search to find and delete files
	//that are not of this Sunday date.  This search is limited to 10 weeks back (604800 seconds).
	//Files with Sunday dates that are older will not be purged by this search and will have to be
	//manually deleted by the user.
	xmlFileName = GetXmlFileName();
   for (k=calendardate;k>=calendardate-6048000;k=k-604800)
      {
      xmlSearchName =  (StringConcatenate(TimeYear(k),"-",
         PadString(DoubleToStr(TimeMonth(k),0),"0",2),"-",
         PadString(DoubleToStr(TimeDay(k),0),"0",2),"-FFCal-News",".xml"));
      xmlHandle = FileOpen(xmlSearchName, FILE_BIN|FILE_READ);
	   if(xmlHandle >= 0) //file exists.  A return of -1 means file does not exist.
	      {
	      FileClose(xmlHandle);
	      if(xmlSearchName != xmlFileName)FileDelete(xmlSearchName);
	      }
	   }

   W= 0; cnr= 2; if(Allow_Panel_At_Chart_Right) {cnr= 3;}
   if(Allow_Panel_In_Subwindow_1 && WindowsTotal( ) > 1){W= 1;}

	SetIndexBuffer(0, ExtMapBuffer0);
   SetIndexStyle(0, DRAW_NONE);
   IndicatorShortName("FFCal");

   Deinitialized = false;
   Alert_Allowed = true;
   
	return(0);
   }

//+-----------------------------------------------------------------------------------------------+
//| Indicator Start                                                                               |
//+-----------------------------------------------------------------------------------------------+
int start()
   {
   //If Indicator is "Off" or chart is out of range deinitialize only once, not every tick.
   if((!Indicator_On) || ((Period() < Display_Min_TF) || (Period() > Display_Max_TF)))
      {
      if (!Deinitialized)  {deinit(); Deinitialized = true;}
      //deleting old versions xml file
      return(0);
      }    

   InitNews(sUrl);

	//qFish-----------------------------------------------------------------------------------------
	//Perform remaining checks once per UpdateRateSec (Refreshing News from XML file)
   if(PrevTF==Period())
      {
      //if we haven't changed time frame then keep doing what we are doing
      if(MathMod(Seconds(),UpdateRateSec)==0)
         {
         return (true);
         }
      //otherwise, we've switched time frame and do not need to skip every 10 s,
      //thus immediately execute all of the start() function code
      else
         {
         PrevTF = Period();
         }
      }

	//Init the buffer array to zero just in case
	ArrayInitialize(ExtMapBuffer0, 0);

	//deVries---------------------------------------------------------------------------------------
	//New xml file handling coding and revised parsing coding
	xmlHandle = FileOpen(xmlFileName, FILE_BIN|FILE_READ);
	if(xmlHandle>=0)
	   {
	   int size = FileSize(xmlHandle);
	   sData = FileReadString(xmlHandle, size);
	   FileClose(xmlHandle);
	   }

	//Clear prioritization data
	BankIdx1   = 0;
	FLAG_1_done = false;
   FLAG_2_done = false;
   FLAG_3_done = false;
   FLAG_4_done = false;
   FLAG_1_none = false;
   FLAG_2_none = false;
   FLAG_3_none = false;
   FLAG_4_none = false;
	for (i=0; i<=9; i++)
	   {
	   Title[i]   = "";
	   Country[i] = "";
	   Impact[i] 	= "";
	   Minutes[i]	= 0;
      }

	//Parse the XML file looking for an event to report
	newsIdx = 0;
	tmpMins = 10080;	// (a week)
	BoEvent = 0;
	while (true)
   	{
		BoEvent = StringFind(sData, "", BoEvent);
		if (BoEvent == -1) break;
		BoEvent += 7;
		next = StringFind(sData, "", BoEvent);
		if (next == -1) break;
		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
				//Get data between start and end tag
				if (end > begin && end != -1)
				   {mainData[newsIdx][i] = StringSubstr(myEvent, begin, end - begin);}
			   }
		   }//End "for" loop

		//Test against filters that define whether we want to skip this particular announcement
		if(!IsNewsCurrency(Symbol(),mainData[newsIdx][COUNTRY]))	//deVries
			{skip = true;}

		else if ((Show_H_HM_HML_123 == 1) &&
		   (mainData[newsIdx][IMPACT] == "Medium"))
		   {skip = true;}

		else if ((Show_H_HM_HML_123 == 1 || Show_H_HM_HML_123 == 2) &&
		   (mainData[newsIdx][IMPACT] == "Low"))
		   {skip = true;}

		else if (!StringSubstr(mainData[newsIdx][TITLE],0,4)== "Bank")
		    {skip = true;}

		else if (!StringSubstr(mainData[newsIdx][TITLE],0,8)== "Daylight")
		    {skip = true;}

   	else if ((mainData[newsIdx][TIME] == "All Day" && mainData[newsIdx][TIME] == "") ||
		   (mainData[newsIdx][TIME] == "Tentative" && mainData[newsIdx][TIME] == "")     ||
		  	(mainData[newsIdx][TIME] == ""))
		  	{skip = true;}

		//If not skipping this event, then log time to event it into ExtMapBuffer0
		if (!skip)
		   {
			//If we got this far then we need to calc the minutes until this event
			//First, convert the announcement time to seconds (in GMT)
			newsTime = MakeDateTime(mainData[newsIdx][DATE], mainData[newsIdx][TIME]);
			// Now calculate the minutes until this announcement (may be negative)
			minsTillNews = (newsTime - TimeGMT()) / 60;

			//At this point, only events applicable to the pair ID/Symbol are being processed:
			//Find the most recent past event.  Do this by incrementing idxOfNext for each event
			//with a past time, (i.e. minsTillNews < 0).  When coming upon the first event with
			//minsTillNews > 0, idxOfNext is not incremented, and therefore continues to be for
			//the most recent past event.  It does not get incremented again until the absolute
			//value of the time since this most recent past event exceeds the time to the next
			//future event.
			if (minsTillNews < 0 || MathAbs(tmpMins) > minsTillNews)
			   {idxOfNext = newsIdx; tmpMins	= minsTillNews;}

			//ExtMapBuffer0 contains the time UNTIL each announcement (can be negative)
			//e.g. [0] = -372; [1] = 25; [2] = 450; [3] = 1768 (etc.)
			ExtMapBuffer0[newsIdx] = minsTillNews;
			newsIdx++;

		   }//End "skip" routine
	   }//End "while" routine

	//----------------------------------------------------------------------------------------------
	//Prioritization coding:  Cycle thru the range of "newsIdx" prioritizing events for display.
	for (i=0; i < newsIdx; i++)
	   {
      //The 1st event to be displayed is either a past event, or an upcoming event, whichever is
      //most most close in time.  If the 1st event is a past event, then look at the three previous
      //past events.  For any of them that occurred at the same time as the 1st event, choose for
      //display the most recent one with a higher impact.  If none have a higher impact, then stay
      //with the 1st event.

      //Get 4th previous item:
      if (i == idxOfNext-3)
         {
			Title[7]	   = mainData[i][TITLE];
			Country[7] 	= mainData[i][COUNTRY];
			Impact[7]  	= mainData[i][IMPACT];
			Minutes[7] 	= ExtMapBuffer0[i];
         }
      //Get 3rd previous item:
      if (i == idxOfNext-2)
         {
			Title[6]	   = mainData[i][TITLE];
			Country[6] 	= mainData[i][COUNTRY];
			Impact[6]  	= mainData[i][IMPACT];
			Minutes[6] 	= ExtMapBuffer0[i];
         }
      //Get 2nd previous item:
      if (i == idxOfNext-1)
         {
			Title[5]	   = mainData[i][TITLE];
			Country[5] 	= mainData[i][COUNTRY];
			Impact[5]  	= mainData[i][IMPACT];
			Minutes[5] 	= ExtMapBuffer0[i];
         }
      //Get previous/current items:
      if (i == idxOfNext)
         {
         Title[1]	   = mainData[i][TITLE];
			Country[1] 	= mainData[i][COUNTRY];
			Impact[1]  	= mainData[i][IMPACT];
			Minutes[1] 	= ExtMapBuffer0[i];
         //If idxOfNext is a previous event, then compare to 2nd, 3rd and 4th previous events.
         //If none of these three also previous events have the same time and a higher impact,
         //stay with the most recent previous event ([0]):  			         
         if (Minutes[0] <= 0)
            {
	         //if time [0] = [4] and [4] is > impact than [0], then [4] becomes 1st event [0]
	         //In other words, if 2nd previous event back is of higher impact use it:
	         if ((Minutes[0] == Minutes[4]) &&
	         ((Impact[1] == "Medium" && Impact[5] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[5] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[5] == "Medium")))
		 	      {
		   	   Title[1]	= Title[5];
			      Country[1] = Country[5];
			      Impact[1]  = Impact[5];
			      Minutes[1] = Minutes[5];
			      }
		      //if time [0] = [5] and [5] is > impact than [0], then [5] becomes 1st event [0]
		      //In other words, if 3rd previous event back is of higher impact use it:
	         else if ((Minutes[0] == Minutes[5]) &&
	         ((Impact[1] == "Medium" && Impact[5] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[5] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[5] == "Medium")))
		 	      {
		      	Title[1]   = Title[6];
			      Country[1] = Country[6];
			      Impact[1]  = Impact[6];
			      Minutes[1] = Minutes[6];
               }
            //if time [0] = [6] and [6] is  > impact than [0], then [6] becomes 1st event [0]
            //In other words, if 4th previous event back is of higher impact use it:
	         else if ((Minutes[0] == Minutes[6]) &&
	         ((Impact[1] == "Medium" && Impact[7] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[7] == "High")  ||
			   (Impact[1]  == "Low"    && Impact[7] == "Medium")))
		 	      {
		      	Title[1]   = Title[7];
			      Country[1] = Country[7];
			      Impact[1]  = Impact[7];
			      Minutes[1] = Minutes[7];
               }
            }
         }

		//-------------------------------------------------------------------------------------------       
      //Now that 1st event[0] is initialized, go thru rest of items, prioritizing. 
		if (i >= idxOfNext+1)
		   {
         Title[7]	= mainData[i][TITLE];
			Country[7] = mainData[i][COUNTRY];
			Impact[7]  = mainData[i][IMPACT];
			Minutes[7] = ExtMapBuffer0[i];
			//If 1st event [1] is not done with prioritization, and
			//if time [i] = [1] and [i] has > impact than [1] then [i] becomes 1st event [1].
	   	//Otherwise, if time [i] > [1] then initialize [i] as 2nd event [2]:
	   	if (!FLAG_1_done)
	   	   {
			   if ((Minutes[1] == Minutes[7]) &&
	            ((Impact[1]  == "Medium" && Impact[7] == "High")  ||
			      (Impact[1]   == "Low"    && Impact[7] == "High")  ||
			      (Impact[1]   == "Low"    && Impact[7] == "Medium")))
		 	      {
               Title[1]	    = Title[7];
			      Country[1]   = Country[7];
			      Impact[1]    = Impact[7];
			      Minutes[1]   = Minutes[7];
			      }
		      else if ((Minutes[1] < Minutes[7]) ||
		         ((Minutes[1] == Minutes[7]) && (Impact[7] == "High")) )
		         {
               Title[2]	    = Title[7];
			      Country[2]   = Country[7];
			      Impact[2]    = Impact[7];
			      Minutes[2]   = Minutes[7];
			      FLAG_1_done = true;
			      continue;
		         }
		      }
		   //If 1st event [1] is done prioritization, but 2nd event [2] is not, and
			//if time [i] = [2] and [i] has > impact than [2], then [i] becomes 2nd event [2].
			//Otherwise, if time [i] > [2] then intialize [i] as 3rd event [3]:
         if ((FLAG_1_done) && (!FLAG_2_done))
		      {			 			   	
			   if ((Minutes[2] == Minutes[7]) &&
	            ((Impact[2]  == "Medium" && Impact[7] == "High")  ||
			      (Impact[2]   == "Low"    && Impact[7] == "High")  ||
			      (Impact[2]   == "Low"    && Impact[7] == "Medium")))
		 	      {
               Title[2]	   = Title[7];
			      Country[2] 	= Country[7];
			      Impact[2]  	= Impact[7];
			      Minutes[2] 	= Minutes[7];
			      }
			   else if ((Minutes[2] < Minutes[7]) ||
		         ((Minutes[2] == Minutes[7]) && (Impact[7] == "High")) )
			      {
               Title[3]	   = Title[7];
			      Country[3] 	= Country[7];
			      Impact[3]  	= Impact[7];
			      Minutes[3] 	= Minutes[7];
			      FLAG_2_done = true;
			      continue;
			      }
			   }
	      //If 1st event [1] and 2nd event [2] are done, but 3rd event [3] is not, and
			//if time [i] = [3] and [i] has > impact than [3], then [i] becomes 3rd event [3].
		   //Otherwise, if time [i] > [3] then intialize [i] as 4th event [4]:
         if ((FLAG_1_done) && (FLAG_2_done) && (!FLAG_3_done))
		      {						   
			   if ((Minutes[3] == Minutes[7]) &&
	            ((Impact[3]  == "Medium" && Impact[7] == "High")  ||
			      (Impact[3]   == "Low"    && Impact[7] == "High")  ||
			      (Impact[3]   == "Low"    && Impact[7] == "Medium")))
		 	      {
               Title[3]	   = Title[7];
			      Country[3] 	= Country[7];
			      Impact[3]  	= Impact[7];
			      Minutes[3] 	= Minutes[7];
			      }
			   else if ((Minutes[3] < Minutes[7]) ||
		         ((Minutes[3] == Minutes[7]) && (Impact[7] == "High")) )
			      {
               Title[4]	   = Title[7];
			      Country[4] 	= Country[7];
			      Impact[4]  	= Impact[7];
			      Minutes[4] 	= Minutes[7];
			      FLAG_3_done = true;
			      continue;
			      }
			   }
         //If 1st, 2nd, and 3rd events are done, but 4th event [4] is not, and
			//if time [i] = [4] and [i] has > impact than [4], then [i] becomes 4th event [4].
			//Otherwise, prioritizing is finished:
         if ((FLAG_1_done) && (FLAG_2_done) && (FLAG_3_done) && (!FLAG_4_done))
		      {						   
			   if ((Minutes[4] == Minutes[7])  &&
	            ((Impact[4]  == "Medium" && Impact[7] == "High")  ||
			      (Impact[4]   == "Low"    && Impact[7] == "High")  ||
			      (Impact[3]   == "Low"    && Impact[7] == "Medium")))
		 	      {
               Title[4]	   = Title[7];
			      Country[4] 	= Country[7];
			      Impact[4]  	= Impact[7];
			      Minutes[4] 	= Minutes[7];
			      }
			   //Otherwise, prioritizing is finished:
			   else
			      {
			      FLAG_4_done = true;
			      }
			   }
 			//If all four event items are prioritized, exit loop.
		   if ((FLAG_1_done) && (FLAG_2_done) && (FLAG_3_done) && (FLAG_4_done)) {break;}
			}
	   }//End prioritizing "for" loop

	   //Next, determine if any events are out of sequence in time, or have duplicate
	   //titles within the same 6 hour period.  If so, eliminate the higher event item
	   //by setting the flag to not show the item.
	   //Check 1st and 2nd items.
		if (((Title[1]==Title[2]) && (Minutes[1]+ 360 > Minutes[2])) ||
		   (Minutes[1] > Minutes[2]))
			{
			{FLAG_2_none  = true;}
			}
      //Also check 1st and 3rd, and 2nd and 3rd items.
		if (((Title[1]==Title[3]) && (Minutes[1]+ 360 > Minutes[3])) ||
			((Title[2]==Title[3]) && (Minutes[2]+ 360 > Minutes[3]))  ||
			(Minutes[1] > Minutes[3]) || (Minutes[2] > Minutes[3]))
			{
			{FLAG_3_none  = true;}
			}
      //Also check 1st and 4th, and 3rd and 4th items.
		if (((Title[1]==Title[4]) && (Minutes[1]+ 360 > Minutes[4])) ||
			((Title[3]==Title[4]) && (Minutes[3]+ 360 > Minutes[4]))  ||
			(Minutes[1] > Minutes[4]) || (Minutes[3] > Minutes[4]))
			{
			{FLAG_4_none  = true;}
			}

	   //Check if 1st event[0] is more than half a day old and no 2nd event is scheduled.
	   //If so, eliminate the 1st event[0].
	   if ((Minutes[1] < -720) && (Minutes[2] == 0 ))
			{
			{FLAG_1_none  = true;}
			}

		//Be sure no "FLAG_none" remains false if a lower one turned to true
		if (FLAG_1_none)
		   {
		   FLAG_2_none  = true;
		   FLAG_3_none  = true;
		   FLAG_4_none  = true;
		   }
		else if (FLAG_2_none)
		   {
		   FLAG_3_none  = true;
		   FLAG_4_none  = true;
		   }
		else if (FLAG_3_none)
		   {
		   FLAG_4_none  = true;
		   }

		//Be sure to "FLAG_none" any with no country tag
		if (Country[1] == "") {FLAG_1_none = true;}
		if (Country[2] == "") {FLAG_2_none = true;}
		if (Country[3] == "") {FLAG_3_none = true;}
		if (Country[4] == "") {FLAG_4_none = true;}

		//Check for current day Bank Holiday.  If there is one, then move all
      //events up one tier and insert the Bank Holiday into 1st event.
      if (BankIdx1 == 1)
		   {
         Title[4]	   = Title[3];
			Country[4] 	= Country[3];
			Impact[4]  	= Impact[3];
			Minutes[4] 	= Minutes[3];
			FLAG_4_none  = FLAG_3_none;

			Title[3]	   = Title[2];
			Country[3] 	= Country[2];
			Impact[3]  	= Impact[2];
			Minutes[3] 	= Minutes[2];
			FLAG_3_none  = FLAG_2_none;

         Title[2]	   = Title[1];
			Country[2] 	= Country[1];
			Impact[2]  	= Impact[1];
			Minutes[2] 	= Minutes[1];
			FLAG_2_none  = FLAG_1_none;

         Title[1]	   = Title[8];
			Country[1]  = Country[8];
			Impact[1]  	= Impact[8];
			Minutes[1]  = Minutes[8];
			FLAG_1_none  = false;
			}

   //Set up Panel events---------------------------------------------------------------------------
   if(!Allow_Panel_At_Chart_Right) curX = 4; else curX = 2;
   curY = 4;

   //Event4
   sinceUntil = "until ";
   dispMins = Minutes[4]+1;
	if (Minutes[4] <= 0) {dispMins = dispMins - 1; sinceUntil = "since "; dispMins *= -1;}
   if (dispMins < 60) {TimeStr = dispMins + " mins ";}
	else // time is 60 minutes or more
	   {
		Hours = MathRound(dispMins / 60);
	   Mins = dispMins % 60;
	   if (Hours < 24) // less than a day: show hours and minutes
		   {
			TimeStr = Hours + " hrs " + Mins + " mins ";
		   }
		else // days, hours, and minutes
		  	{
		  	Days = MathRound(Hours / 24);
		 	Hours = Hours % 24;
		  	TimeStr = Days + " days " + Hours + " hrs " + Mins + " mins ";
		  	}
	   }
   index = StringFind(TimeStr+sinceUntil+Country[4], "since  mins", 0);
   TxtColorNews = ImpactToColor(Impact[4]);
	if((index != -1) || (FLAG_4_none))
	   {
	   event[4] = "(4)  Additional news per settings is not scheduled  ";
	   Color[4] = Remarks_Color;
	   }
	else
	   {
	   event[4] = TimeStr + sinceUntil + Country[4] + ": " + Title[4] + "  ";
	   Color[4] = TxtColorNews;
		}

   //Event3
   sinceUntil = "until ";
   dispMins = Minutes[3]+1;
	if (Minutes[3] <= 0) {dispMins = dispMins - 1; sinceUntil = "since "; dispMins *= -1;}
   if (dispMins < 60) {TimeStr = dispMins + " mins ";}
	else // time is 60 minutes or more
	   {
		Hours = MathRound(dispMins / 60);
	   Mins = dispMins % 60;
	   if (Hours < 24) // less than a day: show hours and minutes
		   {
			TimeStr = Hours + " hrs " + Mins + " mins ";
		   }
		else // days, hours, and minutes
		  	{
		  	Days = MathRound(Hours / 24);
		 	Hours = Hours % 24;
		  	TimeStr = Days + " days " + Hours + " hrs " + Mins + " mins ";
		  	}
	   }
   index = StringFind(TimeStr+sinceUntil+Country[3], "since  mins", 0);
   TxtColorNews = ImpactToColor(Impact[3]);
	if((index != -1) || (FLAG_3_none))
		{
		event[3] = "(3)  Additional news per settings is not scheduled  ";
		Color[3] = Remarks_Color;
		}
	else
	   {
	   event[3] = TimeStr + sinceUntil + Country[3] + ": " + Title[3] + "  ";
	   Color[3] = TxtColorNews;
		}

   //Event2
   sinceUntil = "until ";
   dispMins = Minutes[2]+1;
	if (Minutes[2] <= 0) {dispMins = dispMins - 1; sinceUntil = "since "; dispMins *= -1;}
   if (dispMins < 60) {TimeStr = dispMins + " mins ";}
	else // time is 60 minutes or more
	   {
	   Hours = MathRound(dispMins / 60);
	   Mins = dispMins % 60;
	   if (Hours < 24) // less than a day: show hours and minutes
		   {
		   TimeStr = Hours + " hrs " + Mins + " mins ";
		   }
		else // days, hours, and minutes
		   {
		   Days = MathRound(Hours / 24);
			Hours = Hours % 24;
			TimeStr = Days + " days " + Hours + " hrs " + Mins + " mins ";
		   }
	   }
	index = StringFind(TimeStr+sinceUntil+Country[2], "since  mins", 0);
   TxtColorNews = ImpactToColor(Impact[2]);
	if((index != -1) || (FLAG_2_none))
		{	
		event[2] = "(2)  Additional news per settings is not scheduled  ";
		Color[2] = Remarks_Color;
      }
	else
	   {
	   event[2] = TimeStr + sinceUntil + Country[2] + ": " + Title[2] + "  ";
	   Color[2] = TxtColorNews;
		}

   //Event1
	//If time is 0 or negative, we want to say "xxx mins SINCE ... news event",
	//else say "UNTIL ... news event"
	sinceUntil = "until ";
	dispMins = Minutes[1]+1;  //dispMins "*= -1" = multiply by "-1"
	if (Minutes[1] <= 0) {dispMins = dispMins - 1; sinceUntil = "since "; dispMins *= -1;}
	if (dispMins < 60) {TimeStr = dispMins + " mins ";}
	else // time is 60 minutes or more
	   {
		Hours = MathRound(dispMins / 60);
		Mins = dispMins % 60;
		if (Hours < 24) // less than a day: show hours and minutes
		   {
		   TimeStr = Hours + " hrs " + Mins + " mins ";
		   }
		else  // days, hours, and minutes
		   {
		   Days = MathRound(Hours / 24);
			Hours = Hours % 24;
			TimeStr = Days + " days " + Hours + " hrs " + Mins + " mins ";
		   }
	   }
	index = StringFind(TimeStr+sinceUntil+Country[1], "since  mins", 0); //Comment (index);
	TxtColorNews = ImpactToColor(Impact[1]);
	//return of "-1" means no previous/current/future event to display
	if((index != -1) || (FLAG_1_none))
		{
		event[1] = "(1)  Additional news per settings is not scheduled  ";
		Color[1] = Remarks_Color;
		}
	else  // will display "last" event until a "next" event is scheduled
		{
		event[1] = TimeStr + sinceUntil + Country[1] + ": " + Title[1] + "  ";
		Color[1] = TxtColorNews;
	   }

	//Execute draw of Panel-------------------------------------------------------------------------
   //Setup Panel background
   x1 = 0;
   x2 = 0;

   //Determine the length (x1) of the maximum length event string (i = 1-4)
   for(i=4; i>=1; i--)
      {
      if(StringSubstr(event[i], 0, 1) != "(")
        {
        if(i == 4) {x1 = StringLen(event[4]);}
        else {if(StringLen(event[i]) > x1) {x1 = StringLen(event[i]);}}
        }
      }

   //The panel with just the heading and no events requires a width that is the equivalent of a
   //50 character event.  For the panel to have the proper width requires panel box 1 start at
   //the chart border and panel box 2 be inset 23 pixels from the border.  if any event exceeds
   //50 characters then panel box 2 must be inset an addtional 2 pixels plue 4 more for each
   //additional character.
   int c = 50;
   if(x1Minutes[1])
	      {
	      header = "FFCAL  (Alert Completed):";
	      }
      }

	ObjectDelete(Sponsor);
	ObjectCreate(Sponsor, OBJ_LABEL, W, 0, 0);
	ObjectSet(Sponsor, OBJPROP_CORNER, cnr);
	ObjectSet(Sponsor, OBJPROP_XDISTANCE, curX);
	ObjectSet(Sponsor, OBJPROP_YDISTANCE, curY);
	ObjectSet(Sponsor, OBJPROP_BACK, false);
	ObjectSetText(Sponsor, header, TxtSize, "Verdana", FFCal_Title);
	return (0);
   }

//+-----------------------------------------------------------------------------------------------+
//| Subroutines: recoded creation and maintenance of single xml file                              |
//+-----------------------------------------------------------------------------------------------+   
//deVries: void InitNews(string& mainData[][], string newsUrl)
void InitNews(string newsUrl)
   {
   if(DoFileDownLoad()) //Added to check if the CSV file already exists
      {
      DownLoadWebPageToFile(newsUrl); //downloading the xml file
      }
   }

//deVries: If we have recent file don't download again
bool DoFileDownLoad()
   {
   xmlHandle = 0;
   int size;
   datetime time = TimeCurrent();
   //datetime time = TimeLocal();

   if(GlobalVariableCheck("Update.FF_Cal") == false)return(true);
   if((time - GlobalVariableGet("Update.FF_Cal")) > WebUpdateFreq*60)return(true);

   xmlFileName = GetXmlFileName();
   xmlHandle=FileOpen(xmlFileName,FILE_BIN|FILE_READ);  //check if file exist
   if(xmlHandle>=0)//when the file exists we read data
      {
	   size = FileSize(xmlHandle);
	   sData = FileReadString(xmlHandle, size);
      FileClose(xmlHandle);//close it again check is done
      return(false);//file exists no need to download again
      }
   //File does not exist if FileOpen return -1 or if GetLastError = ERR_CANNOT_OPEN_FILE (4103)
   return(true); //commando true to download xml file
   }

//+-----------------------------------------------------------------------------------------------+
//| Subroutine: getting the name of the ForexFactory .xml file                                    |
//+-----------------------------------------------------------------------------------------------+
//deVries: one file for all charts!
string GetXmlFileName()
   {
   int adjustDays = 0;
   switch(TimeDayOfWeek(TimeLocal()))
      {
      case 0:
      adjustDays = 0;
      break;
      case 1:
      adjustDays = 1;
      break;
      case 2:
      adjustDays = 2;
      break;
      case 3:
      adjustDays = 3;
      break;
      case 4:
      adjustDays = 4;
      break;
      case 5:
      adjustDays = 5;
      break;
      case 6:
      adjustDays = 6;
      break;
      }
   calendardate =  TimeLocal() - (adjustDays  * 86400);
   string fileName =  (StringConcatenate(TimeYear(calendardate),"-",
          PadString(DoubleToStr(TimeMonth(calendardate),0),"0",2),"-",
          PadString(DoubleToStr(TimeDay(calendardate),0),"0",2),"-FFCal-News",".xml"));

   return (fileName); //Always a Sunday date
   }

//+-----------------------------------------------------------------------------------------------+
//| Subroutine: downloading the ForexFactory .xml file                                            |
//+-----------------------------------------------------------------------------------------------+
//2014 deVries: new coding replacing old "GrabWeb" coding
//2015 atstrader: revise file access coding
void DownLoadWebPageToFile(string url = "http://www.forexfactory.com/ff_calendar_thisweek.xml")
   {
   int HttpOpen = InternetOpenW(" ", 0, " ", " ", 0);
   int HttpConnect = InternetConnectW(HttpOpen, "", 80, "", "", 3, 0, 1);

//start 2015 atstrader revision
   // int HttpRequest = InternetOpenUrlW(HttpOpen, url, NULL, 0, 0, 0);
   // INTERNET_FLAG_PRAGMA_NOCACHE - 0x00000100 - do not try the cache or proxy
   // INTERNET_FLAG_NO_CACHE_WRITE - 0x04000000 - don't add this to the IE cache
   // INTERNET_FLAG_RELOAD         - 0x80000000 - Forces download of requested file, object, or
   //                                directory listing from the origin server, not from the cache.
   int HttpRequest = InternetOpenUrlW(HttpOpen, url, NULL, 0, 0x84000100, 0);
//end 2015 atstrader revision

   int read[1];
   uchar  Buffer[];
   ArrayResize(Buffer, READURL_BUFFER_SIZE + 1);
   string NEWS = "";

	   xmlFileName = GetXmlFileName();
	   xmlHandle = FileOpen(xmlFileName, FILE_BIN|FILE_READ|FILE_WRITE);
	   //File exists if FileOpen return >=0.
	   if (xmlHandle >= 0) {FileClose(xmlHandle); FileDelete(xmlFileName);}

		//Open new XML.  Write the ForexFactory page contents to a .htm file.  Close new XML.
		xmlHandle = FileOpen(xmlFileName, FILE_BIN|FILE_WRITE);

   while (true)
      {
      InternetReadFile(HttpRequest, Buffer, READURL_BUFFER_SIZE, read);
      string strThisRead = CharArrayToString(Buffer, 0, read[0], CP_UTF8);
      if (read[0] > 0)NEWS = NEWS + strThisRead;
      else
         {
         FileWriteString(xmlHandle, NEWS);
         FileClose(xmlHandle);
		   //Find the XML end tag to ensure a complete page was downloaded.
		   end = StringFind(NEWS, "", 0);
		   //If the end of file tag is not found, a return -1 (or, "end <=0" in this case),
		   //then return (false).
		   if (end == -1)
		      {
		      Alert(Symbol()," ",Period(),", FFCal Error: File download incomplete!");
		      return;
		      }
		   //Else, set global to time of last update
		   else {GlobalVariableSet("Update.FF_Cal", TimeCurrent());}
         break;
         }
      }
   if (HttpRequest > 0) InternetCloseHandle(HttpRequest);
   if (HttpConnect > 0) InternetCloseHandle(HttpConnect);
   if (HttpOpen > 0) InternetCloseHandle(HttpOpen);
   }

//+-----------------------------------------------------------------------------------------------+
//| Subroutine: to pad string                                                                     |
//+-----------------------------------------------------------------------------------------------+
//deVries:
string PadString(string toBePadded, string paddingChar, int paddingLength)
   {
   while(StringLen(toBePadded) <  paddingLength)
      {
      toBePadded = StringConcatenate(paddingChar,toBePadded);
      }
   return (toBePadded);
   }

//+-----------------------------------------------------------------------------------------------+
//| Subroutine: to ID currency even if broker has added a prefix to the symbol, and is used to    |
//| determine the news to show, based on the users external inputs selections                     |
//+-----------------------------------------------------------------------------------------------+
//deVries: works even when broker has attached prefix (or suffix) to the Currency, so that the
//symbol format does not look like the standard example EURUSD or USDJPY.
//atstrader: adds option to ignore the chart symbol pair and re-select other(s).
bool IsNewsCurrency(string cSymbol, string fSymbol)
   {
   if(!Ignore_Current_Symbol && (StringFind(cSymbol, fSymbol, 0)>=0)){return(true);}
   if(Show_USD_News && fSymbol == "USD"){return(true);}
   if(Show_GBP_News && fSymbol == "GBP"){return(true);}
   if(Show_EUR_News && fSymbol == "EUR"){return(true);}
   if(Show_CAD_News && fSymbol == "CAD"){return(true);}
   if(Show_AUD_News && fSymbol == "AUD"){return(true);}
   if(Show_CHF_News && fSymbol == "CHF"){return(true);}
   if(Show_JPY_News && fSymbol == "JPY"){return(true);}
   if(Show_NZD_News && fSymbol == "NZD"){return(true);}
   if(Show_CNY_News && fSymbol == "CNY"){return(true);}
   return(false);
   }

//+-----------------------------------------------------------------------------------------------+
//| Indicator Subroutine For Impact Color                                                         |
//+-----------------------------------------------------------------------------------------------+
double ImpactToColor (string impact)
   {
	if (impact == "High") return (High_Impact_News_Color);
	else {if (impact == "Medium") return (Medium_Impact_News_Color);
	else {if (impact == "Low") return (Low_Impact_News_Color);
	else {if (impact == "Holiday") return (Bank_Holiday_Color);
	else {return (Remarks_Color);} }}}
   }

//+-----------------------------------------------------------------------------------------------+
//| Indicator Subroutine For Date/Time    changed by deVries                                      |
//+-----------------------------------------------------------------------------------------------+
datetime MakeDateTime(string strDate, string strTime)  //not string now datetime
   {
	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, 4);

	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;}
	if ((strAM_PM == "am" || strAM_PM == "AM") && nHour24 == 12) {nHour24 = 0;}

	datetime newsevent = StringToTime(strYear+ "." + strMonth + "." +
	   strDay)+nHour24*3600+ (StringToInteger(strMinute)*60);
	return(newsevent);
   }

//+-----------------------------------------------------------------------------------------------+
//| Indicator End                                                                                 |
//+-----------------------------------------------------------------------------------------------+

Recommend