//============================================================================= // FFCal_v04.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 to lessen sudden // traffic on the FF site, it refreshes every hour on a random minute. // // // EXAMPLE CODE FOR USE IN AN EA: // (NOTE I HAVE PUT IN CODE TO CALL THE INDICATOR ONLY ONCE PER MINUTE) // // int prevMinute = -1 // if (Minute() != PrevMinute) // { // PrevMinute = Minute(); // // int minutesSincePrevEvent = // iCustom(NULL, 0, "IndFFCal", true, true, false, true, 1, 0); // // int impactOfPrevEvent = // iCustom(NULL, 0, "IndFFCal", true, true, false, true, 2, 0); // // int minutesUntilNextEvent = // iCustom(NULL, 0, "IndFFCal", true, true, false, true, 1, 1); // // int impactOfNextEvent = // iCustom(NULL, 0, "IndFFCal", true, true, false, true, 2, 1); // } // (where impact of 1 = Low, 2 = Medium, and 3 = High) // //============================================================================= // // 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]; static bool FirstRun = true; static int PrevMinute = -1; static int RefreshMin = 0; static int RefreshHour = 0; datetime LastTimeAlert1=0; //used to make sure we only draw something once per annoucement. Added by MN int init() { if (DebugLevel > 0) Print("In Init()...\n"); SetIndexStyle(0, DRAW_NONE); SetIndexBuffer(0, ExtMapBuffer0); SetIndexStyle(1, DRAW_NONE); SetIndexBuffer(1, ExtMapBuffer1); SetIndexStyle(1, DRAW_NONE); SetIndexBuffer(2, ExtMapBuffer2); IndicatorShortName("FFCal"); // SetIndexLabel(0, "MinsBeforeNews"); // SetIndexLabel(1, "MinsAfterNews"); MathSrand(TimeLocal()); RefreshMin = (MathRand() % 60); RefreshHour = (MathRand() % 4); if (DebugLevel > 0) { Print("RefreshMin == ", RefreshMin); Print("RefreshHour == ", RefreshHour); } return(0); } int deinit() { ObjectDelete("Sponsor"); ObjectDelete("Minutes"); ObjectDelete("Impact"); ObjectDelete("Previous"); ObjectDelete("Forecast"); 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 every 4 hours (randomly) // This is to avoid over-trafficing the ForexFactory site if (FirstRun || (Minute() == RefreshMin && (Hour() % RefreshHour == 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 annoucement 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; // This "if" section 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; } // This "if" section added by MN // 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] = MathAbs(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(); 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, Height = 0.0; // Calculate our pivot point to determine where to place the news text Pivot = (iHigh(NULL, 1440, 1) + iLow(NULL, 1440, 1) + iClose(NULL, 1440, 1)) / 3; // 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); // Put object in the background behind any other object on the chart ObjectSet("vLine" + Time[0], OBJPROP_BACK, true); } // Place our news if it hasnt already been placed on our chart if (ObjectFind("Headline" + Time[0]) == -1) { // For 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); // Rotate the text 90 degrees ObjectSet("Headline" + Time[0], OBJPROP_ANGLE, 90); ObjectSetText("Headline" + Time[0], "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor); } } // Draw second news headline else { //place our news if it hasnt already been placed on our chart if (ObjectFind("Headline" + Time[0]+"s") == -1) { // For 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); // Rotate the text 90 degrees ObjectSet("Headline" + Time[0] + "s", OBJPROP_ANGLE, 90); 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; double Pivot; double Height = 0.0; // We have TheTime, now we need the shift so we can // access this Bar's High/Low/Close information BarShift = iBarShift(NULL, 0, TheTime); // Calculate our pivot point to determine where to place the news text Pivot = (iHigh(NULL, 1440, BarShift) + iLow(NULL, 1440, BarShift) + iClose(NULL, 1440, BarShift)) / 3; // 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; // Otherwise Open is below Pivot else 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); // put object in the background behind any other object on the chart ObjectSet("vLine" + TheTime, OBJPROP_BACK, true); } // Place our news if it hasnt already been placed on our chart if (ObjectFind("Headline" + TheTime) == -1) { // For 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); // rotate the text 90 degrees ObjectSet("Headline" + TheTime, OBJPROP_ANGLE, 90); ObjectSetText("Headline" + TheTime, "News: " + dispCountry + " " + dispTitle, VertTxtSize, "Arial", VertTxtColor); } } // Draw second news headline else { //place our news if it hasnt already been placed on our chart if (ObjectFind("Headline" + TheTime + "s") == -1) { // For 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); // Rotate the text 90 degrees ObjectSet("Headline" + TheTime + "s", OBJPROP_ANGLE, 90); 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() { // Added by Robert for using TxtSize and TxtColor for easier reading int curY = 12; 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); // If the time is 0 or negative, we want to say // "xxx mins SINCE ... news event", else say "UNTIL ... news event" string sinceUntil = "until "; int dispMins = dispMinutes[0]; if (dispMinutes[0] <= 0) { sinceUntil = "since "; dispMins *= -1; } curY = curY + TxtSize + 4; ObjectCreate("Minutes", OBJ_LABEL, 0, 0, 0); ObjectSetText("Minutes", dispMins + " Mins " + sinceUntil + 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); } double 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 ===================================== //================================================================================================= //=================================================================================================