//+-------------------------------------------------------------------------------------------------------+ //| FFC.mq4 | //| Copyright © 2016, DerkWehler, | //| traderathome,| //| deVries, | //| qFish, | //| atstrader, | //| awran5, | //| Xronos | //| Last Modifications, 2017 | //|-------------------------------------------------------------------------------------------------------+ /* 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+, and which has resulted in faster code execution. (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 a neat new option controlling for what pair/pairs(s) news is shown. awran5 - Rewrote the indicator all mods can be found here https://www.mql5.com/en/code/15931 - Traderathome, 03-17-2014 email: traderathome@msn.com -------------------------------------------- MAIN MODIFICATIONS -------------------------------------------- ------------------------------------------------------------------------------------------------------------ These modifications were applied to 08-13-2017 By Xronos Started : Version control system (Only one release per day per user, prefered release time 1h before midnight GMT) Specification : At released filename : filename_YYM.MDD.mq4 Inside released source file : #property version "YYM.MDD"//(username) Ex.:Release Date 2017/08/14 YYYY/MM/DD YYM.MDD At released filename : ffc_170.814.mq4 Inside released source file : #property version "170.814"//(Xronos) Added: Execute main rutine only once per minute Added: Display the number of the day in month aside with day name Improved: IgnoreKeyword, FindKeyword functions Fixed: Reading events from xml file Fixed: Selecting events These modifications were applied to 08-01-2017 By Xronos Added: Ability to change ff download url Added: Option to force fresh download every day Added: Option to force fresh download every time Fixed: NO MORE EVENTS issue These modifications were applied to 03-17-2014 release founded here: http://www.forexfactory.com/showthread.php?t=114792 Added: #property strict for compatibility with MT4 Build 600+ and better code quality. Added: Show event Previous/Forecast. (colored impact) Added: Option to show events related to active chart only (override other parameters) Added: Keyword filter. Find or Ignore a specific one word, i.e. "NFP", will filter out all events with/without only that word. (case-sensitive) Added: Option to show currency strength / bar time left / spread value. Added: Option to control the time for display of past events (in minutes). Added: Option to set panel location in 4 chart corners. Added: Tool-tip on mouse hover that shows event title, impact and event time left. Added: Panel title. You can use it as a reminder note :) Modified: Show event in "Date/Time format" instead of minutes left (you can show minutes left through Tool-tip) Modified: Vertical lines for the upcoming events. (You may need to modify the "time offset" input depending on your broker time). Modified: Buffers that holds the upcoming event minute and impact (see examples below) Modified: Revised order of external inputs. Improved: Replaced DownLoadWebPageToFile() function with native Windows URLDownloadToFileW() function. Improved: Placed (download/read XML file) and related codes into a functions so we can call them when needed. Improved: Replaced "GlobalVariables" that used to update XML file with FILE_MODIFY_DATE - OnTimer() set by "AllowUpdates", "UpdateHour" inputs. Improved: Time GMT offset, now the events will show in your local time automatically. Improved: Justify the panel/text when set to right/left. Improved: Alert function, Now it will send beside the Popup Alert: sound alert, Push notification and Emails. (two separate alerts) Improved: Code quality, now the indicator is lighter and faster than ever. Fixed: Various bug fixes, some unnecessary/unused codes or variables removed, placed some variables inside their related functions. -------------------------------------------- HOW TO USE BUFFERS -------------------------------------------- ------------------------------------------------------------------------------------------------------------ For use in an EA, the indicator holds 2 buffers: - Buffer (0) Contains minutes until the most recent event - Buffer (1) Contains impact value for the most recent event (Low = 1, Medium = 2, High = 3) *Please note that the indicator will not work on strategy tester ------------------------------------------------- EXAMPLES ------------------------------------------------- ------------------------------------------------------------------------------------------------------------ Simple call: ------------- NOTE: if you use the simple call method, default values will be applied int EventMinute = (int)iCustom(NULL,0,"FFC",0,0); if(EventMinute == 30) { .. YOUR CODE .. } 30 minutes before the event int EventImpact = (int)iCustom(NULL,0,"FFC",1,0); if(EventImpact == 3) { .. YOUR CODE .. } High impact event Advanced call: ------------- iCustom( string NULL, // symbol int 0, // timeframe string "FFC", // path/name of the custom indicator compiled program bool true, // true/false: Active chart only bool true, // true/false: Include High impact bool true, // true/false: Include Medium impact bool true, // true/false: Include Low impact bool true, // true/false: Include Speaks bool false, // true/false: Include Holidays string "", // Find keyword string "", // Ignore keyword bool true, // true/false: Allow Updates int 4, // Update every (in hours) int 0, // Buffers: (0) Minutes, (1) Impact int 0 // shift ); - Awran5, 08-14-2016 email: awran5@yahoo.com -------------------------------------------------- HEADER ---------------------------------------------------- -------------------------------------------------------------------------------------------------------------- */ #property copyright "Copyright © 2009-2017, traderathome, deVries, qFish, atstrader, awran5, Xronos" #property link "awran5@yahoo.com" #property description "Modified version of FF Calendar Indicator with new features" #property version "170.813"//(Xronos) #property strict #property indicator_chart_window #property indicator_buffers 2 //--- to download the xml #import "urlmon.dll" int URLDownloadToFileW(int pCaller,string szURL,string szFileName,int dwReserved,int Callback); #import //--- #define INAME "FFC" #define TITLE 0 #define COUNTRY 1 #define DATE 2 #define TIME 3 #define IMPACT 4 #define FORECAST 5 #define PREVIOUS 6 //-------------------------------------------- EXTERNAL VARIABLE --------------------------------------------- //------------------------------------------------------------------------------------------------------------ extern bool ReportActive = false; // Report for active chart only (override other inputs) extern bool IncludeHigh = true; // Include high extern bool IncludeMedium = true; // Include medium extern bool IncludeLow = true; // Include low extern bool IncludeSpeaks = true; // Include speaks extern bool IncludeHolidays = false; // Include holidays extern string FindKeyword = ""; // Find keyword extern string IgnoreKeyword = ""; // Ignore keyword extern bool AllowUpdates = true; // Allow updates extern int UpdateHour = 4; // Update every (in hours) input string lb_0 = ""; // ------------------------------------------------------------ input string lb_1 = ""; // ------> PANEL SETTINGS extern bool ShowPanel = true; // Show panel extern bool AllowSubwindow = false; // Show Panel in sub window extern ENUM_BASE_CORNER Corner = 2; // Panel side extern string PanelTitle="Forex Calendar @ Forex Factory"; // Panel title extern color TitleColor = C'46,188,46'; // Title color extern bool ShowPanelBG = true; // Show panel backgroud extern color Pbgc = C'25,25,25'; // Panel backgroud color extern color LowImpactColor = C'91,192,222'; // Low impact color extern color MediumImpactColor = C'255,185,83'; // Medium impact color extern color HighImpactColor = C'217,83,79'; // High impact color extern color HolidayColor = clrOrchid; // Holidays color extern color RemarksColor = clrGray; // Remarks color extern color PreviousColor = C'170,170,170'; // Forecast color extern color PositiveColor = C'46,188,46'; // Positive forecast color extern color NegativeColor = clrTomato; // Negative forecast color extern bool ShowVerticalNews = true; // Show vertical lines extern int ChartTimeOffset = 0; // Chart time offset (in hours) extern int EventDisplay = 10; // Hide event after (in minutes) input string lb_2 = ""; // ------------------------------------------------------------ input string lb_3 = ""; // ------> SYMBOL SETTINGS extern bool ReportForUSD = true; // Report for USD extern bool ReportForEUR = true; // Report for EUR extern bool ReportForGBP = true; // Report for GBP extern bool ReportForNZD = true; // Report for NZD extern bool ReportForJPY = true; // Report for JPY extern bool ReportForAUD = true; // Report for AUD extern bool ReportForCHF = true; // Report for CHF extern bool ReportForCAD = true; // Report for CAD extern bool ReportForCNY = false; // Report for CNY input string lb_4 = ""; // ------------------------------------------------------------ input string lb_5 = ""; // ------> INFO SETTINGS extern bool ShowInfo = true; // Show Symbol info ( Strength / Bar Time / Spread ) extern color InfoColor = C'255,185,83'; // Info color extern int InfoFontSize = 8; // Info font size input string lb_6 = ""; // ------------------------------------------------------------ input string lb_7 = ""; // ------> NOTIFICATION input string lb_8 = ""; // *Note: Set (-1) to disable the Alert extern int Alert1Minutes = 30; // Minutes before first Alert extern int Alert2Minutes = -1; // Minutes before second Alert extern bool PopupAlerts = false; // Popup Alerts extern bool SoundAlerts = true; // Sound Alerts extern string AlertSoundFile = "news.wav"; // Sound file name extern bool EmailAlerts = false; // Send email extern bool NotificationAlerts= false; // Send push notification extern string ffDownLoadUrl ="http://www.forexfactory.com/ff_calendar_thisweek.xml"; // Forex Factory calendar download url //alternative url "http://www.forexfactory.com/ff_calendar_thisweek.xml","https://cdn-nfs.forexfactory.net/ff_calendar_thisweek.xml" extern bool ForceNewDownloadEveryDay=true; extern bool ForceNewDownloadEveryTime=false; //------------------------------------------------------------------------------------------------------------ //--------------------------------------------- INTERNAL VARIABLE -------------------------------------------- //--- Vars and arrays string ImpHigh="high"; string ImpMedium="medium"; string ImpLow="low"; string ImpHoliDay="holiday"; string TitSpeaks="speaks"; string TimAllDay="All Day"; string TimTentative="Tentative"; double TimeTag; /////////////////////////// string xmlFileName; string sData; string Event[200][7]; string eTitle[10],eCountry[10],eImpact[10],eForecast[10],ePrevious[10]; int eMinutes[10]; datetime eTime[10]; int anchor,x0,x1,x2,xf,xp; int Factor; //--- Alert bool FirstAlert; bool SecondAlert; datetime AlertTime; //--- Buffers double MinuteBuffer[]; double ImpactBuffer[]; //--- time datetime xmlModifed; int TimeOfDay; datetime Midnight; bool IsEvent; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- check for DLL // if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) // { // Alert(INAME+": Please Allow DLL Imports!"); // return(INIT_FAILED); // } //--- indicator buffers mapping SetIndexBuffer(0,MinuteBuffer); SetIndexBuffer(1,ImpactBuffer); SetIndexStyle(0,DRAW_NONE); SetIndexStyle(1,DRAW_NONE); //--- 0 value will not be displayed SetIndexEmptyValue(0,0.0); SetIndexEmptyValue(1,0.0); //--- 4/5 digit brokers if(Digits%2==1) Factor=10; else Factor=1; //--- get today time TimeOfDay=(int)TimeLocal()%86400; Midnight=TimeLocal()-TimeOfDay; //--- set xml file name ffcal_week_this (fixed name) xmlFileName=INAME+"-ffcal_week_this.xml"; //--- checks the existence of the file. if(!FileIsExist(xmlFileName)) { xmlDownload(ffDownLoadUrl); xmlRead(); } //--- else just read it else xmlRead(); //--- get last modification time xmlModifed=(datetime)FileGetInteger(xmlFileName,FILE_MODIFY_DATE,false); //--- check for updates if(AllowUpdates) { if(xmlModifed","","","","]]>","]]>","]]>","]]>","]]>"}; int index=0; int next=-1; int BoEvent=0,begin=0,end=0; string myEvent=""; //--- Minutes calculation datetime EventTime=0; int EventMinute=0; //--- split the currencies into the two parts string MainSymbol=StringSubstr(Symbol(),0,3); string SecondSymbol=StringSubstr(Symbol(),3,3); //--- loop to get the data from xml tags 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; for(int i=0; i<7; i++) { Event[index][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... //--- Advance past the start tag begin=next+StringLen(sTags[i]); end=StringFind(myEvent,eTags[i],begin); //---Find start of end tag and Get data between start and end tag if(end>begin && end!=-1) Event[index][i]=StringSubstr(myEvent,begin,end-begin); if(i==IMPACT || i==TITLE || i==TIME){StringToLower(Event[index][i]);} } } //--- filters that define whether we want to skip this particular currencies or events if(ReportActive && MainSymbol!=Event[index][COUNTRY] && SecondSymbol!=Event[index][COUNTRY]) continue; if(!IsCurrency(Event[index][COUNTRY])) continue; if(!IncludeHigh && Event[index][IMPACT] ==ImpHigh) continue; // Print("Event[index][IMPACT]=",Event[index][IMPACT]); if(!IncludeMedium && Event[index][IMPACT] ==ImpMedium) continue; if(!IncludeLow && Event[index][IMPACT] ==ImpLow) continue; if(!IncludeSpeaks && StringFind(Event[index][TITLE],TitSpeaks)!=-1) continue; if(!IncludeHolidays && Event[index][IMPACT]==ImpHoliDay) continue; if(Event[index][TIME]==TimAllDay || Event[index][TIME]==TimTentative || Event[index][TIME]=="") continue; if(FindKeyword!="") { if(StringFind(Event[index][TITLE],FindKeyword)==-1) continue; } if(IgnoreKeyword!="") { if(StringFind(Event[index][TITLE],IgnoreKeyword)!=-1) continue; } //--- sometimes they forget to remove the tags :) if(StringFind(Event[index][TITLE],"")!=-1) StringReplace(Event[index][TITLE],"]]>",""); if(StringFind(Event[index][TITLE],"]]>")!=-1) StringReplace(Event[index][TITLE],"]]>",""); //--- if(StringFind(Event[index][FORECAST],"<")!=-1) StringReplace(Event[index][FORECAST],"<",""); if(StringFind(Event[index][PREVIOUS],"<")!=-1) StringReplace(Event[index][PREVIOUS],"<",""); //--- set some values (dashes) if empty if(Event[index][FORECAST]=="") Event[index][FORECAST]="---"; if(Event[index][PREVIOUS]=="") Event[index][PREVIOUS]="---"; //--- Convert Event time to MT4 time EventTime=datetime(MakeDateTime(Event[index][DATE],Event[index][TIME])); //--- calculate how many minutes before the event (may be negative) EventMinute=int(EventTime-TimeGMT())/60; //--- only Alert once if(EventMinute==0 && AlertTime!=EventTime) { FirstAlert =false; SecondAlert=false; AlertTime=EventTime; } //--- Remove the event after x minutes if(EventMinute+EventDisplay<0) continue; //--- Set buffers MinuteBuffer[index]=EventMinute; ImpactBuffer[index]=ImpactToNumber(Event[index][IMPACT]); index++; } //--- loop to set arrays/buffers that uses to draw objects and alert for(int i=0; i=0; i--) { string name=ObjectName(i); if(StringFind(name,INAME)==0) ObjectDelete(name); } //--- Kill update timer only if removed if(reason==1) EventKillTimer(); //--- } //+-------------------------------------------------------------------------------------------+ //| Download XML file from forexfactory | //| for windows 7 and later file path would be: | //| C:\Users\xxx\AppData\Roaming\MetaQuotes\Terminal\xxxxxxxxxxxxxxx\MQL4\Files\xmlFileName | //+-------------------------------------------------------------------------------------------+ void xmlDownload(string param_url) { static int flag=0; string ADate = TimeToStr(TimeCurrent(),TIME_DATE); string sUrl=param_url; //--- ResetLastError(); if(ForceNewDownloadEveryDay && !ForceNewDownloadEveryTime){ sUrl=sUrl+"?x="+ADate;//force new file each day "http://www.forexfactory.com/ff_calendar_thisweek.xml"; }else if(ForceNewDownloadEveryDay && ForceNewDownloadEveryTime){ sUrl=sUrl+"?x="+ADate+IntegerToString(flag); flag++; }else if(!ForceNewDownloadEveryDay && ForceNewDownloadEveryTime){ sUrl=sUrl+"?x="+IntegerToString(flag); flag++; }//else will remain intact string FilePath=StringConcatenate(TerminalInfoString(TERMINAL_DATA_PATH),"\\MQL4\\files\\",xmlFileName); int FileGet=URLDownloadToFileW(NULL,sUrl,FilePath,0,NULL); if(FileGet==0) PrintFormat(INAME+": %s file downloaded successfully!",xmlFileName); //--- check for errors else PrintFormat(INAME+": failed to download %s file, Error code = %d",xmlFileName,GetLastError()); //--- } //+------------------------------------------------------------------+ //| Read the XML file | //+------------------------------------------------------------------+ void xmlRead() { //--- ResetLastError(); int FileHandle=FileOpen(xmlFileName,FILE_BIN|FILE_READ); if(FileHandle!=INVALID_HANDLE) { //--- receive the file size ulong size=FileSize(FileHandle); //--- read data from the file while(!FileIsEnding(FileHandle)) sData=FileReadString(FileHandle,(int)size); //--- close FileClose(FileHandle); } //--- check for errors else PrintFormat(INAME+": failed to open %s file, Error code = %d",xmlFileName,GetLastError()); //--- } //+------------------------------------------------------------------+ //| Check for update XML | //+------------------------------------------------------------------+ void xmlUpdate() { //--- do not download on saturday if(TimeDayOfWeek(Midnight)==6) return; else { Print(INAME+": check for updates..."); Print(INAME+": delete old file"); FileDelete(xmlFileName); xmlDownload(ffDownLoadUrl); xmlRead(); xmlModifed=(datetime)FileGetInteger(xmlFileName,FILE_MODIFY_DATE,false); PrintFormat(INAME+": updated successfully! last modified: %s",(string)xmlModifed); } //--- } //+------------------------------------------------------------------+ //| Draw panel and events on the chart | //+------------------------------------------------------------------+ void DrawEvents() { string FontName = "Arial"; int FontSize = 8; string eToolTip = ""; //--- draw backbround / date / special note if(ShowPanel && ShowPanelBG) { eToolTip="Hover on the Event!"; Draw("BG","gggg",85,"Webdings",Pbgc,Corner,x0,3,eToolTip); Draw("Date",DayToStr(Midnight)+", "+MonthToStr()+" "+(string)TimeDay(Midnight),FontSize+1,"Arial Black",TitleColor,Corner,x2,95,"Today"); Draw("Title",PanelTitle,FontSize,FontName,TitleColor,Corner,x1,95,"Panel Title"); Draw("Spreator","------",10,"Arial",RemarksColor,Corner,x2,83,eToolTip); } //--- draw objects / alert functions for(int i=0; i<5; i++) { eToolTip=eTitle[i]+"\nCurrency: "+eCountry[i]+"\nTime left: "+(string)eMinutes[i]+" Minutes"+"\nImpact: "+eImpact[i]; eToolTip=StringToProperCase(eToolTip); //--- impact color color EventColor=ImpactToColor(eImpact[i]); //--- previous/forecast color color ForecastColor=PreviousColor; if(ePrevious[i]>eForecast[i]) ForecastColor=NegativeColor; else if(ePrevious[i]0) arrow="p"; string tooltip="Strength / Spread / Candle Time"; Draw(INAME+": info",Label,InfoFontSize,"Calibri",InfoColor,corner,120,20,tooltip); Draw(INAME+": info arrow",arrow,InfoFontSize-2,"Wingdings 3",InfoColor,corner,130,18,tooltip); } //--- } //+------------------------------------------------------------------+ //| draw event text | //+------------------------------------------------------------------+ void Draw(string name,string label,int size,string font,color clr,ENUM_BASE_CORNER c,int x,int y,string tooltip) { //--- name=INAME+": "+name; int windows=0; if(AllowSubwindow && WindowsTotal()>1) windows=1; ObjectDelete(name); ObjectCreate(name,OBJ_LABEL,windows,0,0); ObjectSetText(name,label,size,font,clr); ObjectSet(name,OBJPROP_CORNER,c); ObjectSet(name,OBJPROP_XDISTANCE,x); ObjectSet(name,OBJPROP_YDISTANCE,y); //--- justify text ObjectSet(name,OBJPROP_ANCHOR,anchor); ObjectSetString(0,name,OBJPROP_TOOLTIP,tooltip); ObjectSet(name,OBJPROP_SELECTABLE,0); //--- } //+------------------------------------------------------------------+ //| draw vertical lines | //+------------------------------------------------------------------+ void DrawLine(string name,datetime time,color clr,string tooltip) { //--- name=INAME+": "+name; ObjectDelete(name); ObjectCreate(name,OBJ_VLINE,0,time,0); ObjectSet(name,OBJPROP_COLOR,clr); ObjectSet(name,OBJPROP_STYLE,2); ObjectSet(name,OBJPROP_WIDTH,0); ObjectSetString(0,name,OBJPROP_TOOLTIP,tooltip); //--- } //+------------------------------------------------------------------+ //| Notifications | //+------------------------------------------------------------------+ void setAlerts(string message) { //--- if(PopupAlerts) Alert(message); if(SoundAlerts) PlaySound(AlertSoundFile); if(NotificationAlerts) SendNotification(message); if(EmailAlerts) SendMail(INAME,message); //--- } string StringToProperCase(string sText) { // Example: StringToProperCase("oNe mAn"); // One Man string sep=" ",AResult="",AChar=""; ushort a_rep,u_sep=StringGetCharacter(sep,0); string temp[]; int k=StringSplit(sText,u_sep,temp); if(k>0) { for(int i=0;i3){ AChar=CharToString(StringGetChar(temp[i],0)); StringToUpper(AChar); a_rep=StringGetCharacter(AChar,0); temp[i]=StringSetChar(temp[i],0,a_rep); }else{ if(StringFind(temp[i],"/")==-1){ StringToUpper(temp[i]); } } AResult=AResult+" "+ temp[i]; } AResult=StringTrimLeft(AResult); } return(AResult); } //+--------------------------- END ----------------------------------+