MQL5 Economic Calendar / CalendarValueHistory in backtest - page 2

 

I can see how your CountryIdToCountry function can sometimes be useful, so I added that to the code. Thanks.

I don't have accounts with brokers in different time zones, so I can't answer your question. However, it shouldn't be necessary to use multiple MT5 installations on your machine. Just temporarily changing the account login should be sufficient.

It is only my assumption(!) that the event time is given as GMT time format. At least with this assumption I get correct results that match with time data from other event calendars, whereas server time or local time would give me wrong result.

The event times are saved in the newshistory.bin file exactly as they are downloaded, so no time format conversion happens here. Having access to the correct GMT time is necessary only later for interpretation (like e.g. in the "next()" function that also has the option to show the next event as a vertical line in the chart, which is only possible if the event time can be interpreted correctly). Therefore the function news.GMT() as an alternative the built-in TimeGMT() function can also be accessed externally from within the main EA code. This avoids the problems with using TimeGMT in the tester (cause other than in live mode it wrongly returns the server time instead).

I probably should add alternative overloads to the "next()" function: the next event "relative to a given time" (like e.g. TimeCurrent()) instead of "relative to an index number".

 

I did some minor changes in the News.mqh file and tested with calendar events of last week so that I could easily compare with Metaquotes' official calendar.

Here is a little convenient code for that purpose (you can use that for checking if the real event times also match if you use your broker (with my broker the time data are correct). One stupid thing is that the values (prev./forecast/actual) are supplied as long variables with a separate value for the digits, instead of simply using double variables. I haven't found out yet how to correctly convert that to doubles. You'll see what I mean if you run this code (as always: with an existing newshistory.bin file or at least once on live data):

#include <News.mqh>

CNews news;

int OnInit()
  {
   news.GMT_offset_winter=2;
   news.GMT_offset_summer=3;
   return(INIT_SUCCEEDED);
  }
  
void OnTick()
  {
   news.update();
   chart_print("news event test",0,30,30,30,5,clrLightBlue);
   static string font="Arial";
   static int fontsize=18;
   static color fontclr=clrYellow;
   chart_print("built-in TimeGMT() function: "+TimeToString(TimeGMT()),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("new.GMT() simulated GMT time: "+TimeToString(news.GMT()),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("server time: "+TimeToString(TimeTradeServer()),-1,-1,-1,fontsize,5,fontclr,font);
   int next_event=news.next(news.GMT(),"ALL");
   chart_print(" #next event (index "+IntegerToString(next_event)+"): "+news.eventname[next_event],-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("event time: "+TimeToString(news.event[next_event].time),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("event sector: "+EnumToString(news.event[next_event].sector),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("affected country: "+EnumToString(ENUM_COUNTRY_ID(news.event[next_event].country_id)),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("affected currency: "+news.CountryIdToCurrency(ENUM_COUNTRY_ID(news.event[next_event].country_id)),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("event importance: "+EnumToString(news.event[next_event].importance),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("previous value: "+IntegerToString(news.event[next_event].prev_value),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("forecast value: "+IntegerToString(news.event[next_event].forecast_value),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("actual value: "+IntegerToString(news.event[next_event].actual_value),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("unit: "+EnumToString(news.event[next_event].unit),-1,-1,-1,fontsize,5,fontclr,font);
   chart_print("digits: "+IntegerToString(news.event[next_event].digits),-1,-1,-1,fontsize,5,fontclr,font);
  }
  





// AUXILIARY FUNCTION:

//+----------------------------------------------------------------------+
//| chart print: multiple text lines, optionally  with '#' as separator  |
//+----------------------------------------------------------------------+
int chart_print(string text,int identifier=-1,int x_pos=-1,int y_pos=-1,int fontsize=10,int linespace=2,color fontcolor=clrGray,string font="Arial",string label_prefix="chart_print_",long chart_id=0,int subwindow=0)
  {
   // set message identifier
   //       negative number:      set next identifier
   //       specific number >=0:  replace older messages with same identifier
   static int id=0;
   static int x_static=0;
   static int y_static=0;   
   if (identifier>=0)
     {id=identifier;}
   else
     {id++;}
   ObjectsDeleteAll(0,label_prefix+IntegerToString(id));
   
   if (text!="") //note: chart_print("",n) can be used to delete a specific message
     {
      // initialize or set cursor position
      //       keep last line feed position: set negative number for y_pos
      //       same x position as last message: set negative number for x_pos
      if (x_pos>=0){x_static=x_pos;}
      if (y_pos>=0){y_static=y_pos;}
      
      // get number of lines ('#' sign is used for line feed)
      int lines=1+MathMax(StringReplace(text,"#","#"),0);
      
      // get substrings
      string substring[];
      StringSplit(text,'#',substring);
      
      // print lines
      for (int l=1;l<=lines;l++)
        {
         string msg_label=label_prefix+IntegerToString(id)+", line "+IntegerToString(l);
         ObjectCreate(chart_id,msg_label,OBJ_LABEL,subwindow,0,0);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_XDISTANCE,x_static);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_YDISTANCE,y_static);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_CORNER,CORNER_LEFT_UPPER);
         ObjectSetString(chart_id,msg_label,OBJPROP_TEXT,substring[l-1]);
         ObjectSetString(chart_id,msg_label,OBJPROP_FONT,font);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_FONTSIZE,fontsize);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_COLOR,fontcolor);
         ObjectSetInteger(chart_id,msg_label,OBJPROP_BACK,false); 
         // line feed
         y_static+=fontsize+linespace;
        }
     } 
   return y_static;
  }

and here the News.mqh with the minor changes:

Files:
News.mqh  26 kb
 

Fantastic! I'm loving this!!


next2



i love your next function, but i did a very minor tweak to make it able to select for two currencies and then colour code the result. I added it as another function rather than changing the existing one and named it 'next2'


      int next_event = news.next2(news.GMT(),"EUR","USD");


   int               next2(int start_index,string currency,string currency2,bool show_on_chart,long chart_id);
   int               next2(datetime start_gmt,string currency="ALL",string currency2="ALL",bool show_on_chart=true,long chart_id=0); 


// +------------------------------------------------------------------+
// | get index to next event for given currency                     |
// +------------------------------------------------------------------+
int CNews::next2(int start_index,string currency="ALL",string currency2="ALL",bool show_on_chart=true,long chart_id=0)
  {
   datetime dt_gmt=GMT();
   for (int p=start_index;p<ArraySize(event);p++)
     {
      if 
        (
         (event[p].country_id==CurrencyToCountryId(currency) || event[p].country_id==CurrencyToCountryId(currency2) ||currency=="ALL")
        )
        {
         if (start_index!=p && show_on_chart && !MQLInfoInteger(MQL_OPTIMIZATION)&&(event[p].importance>=2))
           {
            ObjectCreate(chart_id,"event "+IntegerToString(p),OBJ_VLINE,0,event[p].time+TimeTradeServer()-dt_gmt,0);
            ObjectSetInteger(chart_id,"event "+IntegerToString(p),OBJPROP_WIDTH,2);
            if(event[p].country_id==CurrencyToCountryId(currency)){ObjectSetInteger(chart_id,"event "+IntegerToString(p),OBJPROP_COLOR,clrDodgerBlue);}
            if(event[p].country_id==CurrencyToCountryId(currency2)){ObjectSetInteger(chart_id,"event "+IntegerToString(p),OBJPROP_COLOR,clrRed);}
            ObjectCreate(chart_id,"label "+IntegerToString(p),OBJ_TEXT,0,event[p].time+TimeTradeServer()-dt_gmt,SymbolInfoDouble(Symbol(),SYMBOL_BID));
            ObjectSetInteger(chart_id,"label "+IntegerToString(p),OBJPROP_YOFFSET,800);
            ObjectSetInteger(chart_id,"label "+IntegerToString(p),OBJPROP_BACK,true);
            ObjectSetString(chart_id,"label "+IntegerToString(p),OBJPROP_FONT,"Arial");
            ObjectSetInteger(chart_id,"label "+IntegerToString(p),OBJPROP_FONTSIZE,10);
            ObjectSetDouble(chart_id,"label "+IntegerToString(p),OBJPROP_ANGLE,-90);
            if(event[p].country_id==CurrencyToCountryId(currency)){ObjectSetInteger(chart_id,"label "+IntegerToString(p),OBJPROP_COLOR,clrDodgerBlue);}
            if(event[p].country_id==CurrencyToCountryId(currency2)){ObjectSetInteger(chart_id,"label "+IntegerToString(p),OBJPROP_COLOR,clrRed);}
            ObjectSetString(chart_id,"label "+IntegerToString(p),OBJPROP_TEXT,CountryIdToCurrency((ENUM_COUNTRY_ID)event[p].country_id)+"  |  "+eventname[p]+"  |  "+(string)event[p].importance);
           }
         return p;         
        }
     }
   return start_index;
  }
  
int CNews::next2(datetime start_gmt,string currency="ALL",string currency2="ALL",bool show_on_chart=true,long chart_id=0)
  {
   datetime dt_gmt=GMT();
   int index=MathMax(ArraySize(event),1);
   while(event[index-1].time>start_gmt && index>=0){index--;}
   while(event[index].country_id!=CurrencyToCountryId(currency) && event[index].country_id!=CurrencyToCountryId(currency2)  && currency!="ALL"  && currency2!="ALL"){index++;}
   if (show_on_chart && !MQLInfoInteger(MQL_OPTIMIZATION)&&(event[index].importance>=2))
     {
      ObjectCreate(chart_id,"event "+IntegerToString(index),OBJ_VLINE,0,event[index].time+TimeTradeServer()-dt_gmt,0);
      ObjectSetInteger(chart_id,"event "+IntegerToString(index),OBJPROP_WIDTH,2);
      if(event[index].country_id==CurrencyToCountryId(currency)){ObjectSetInteger(chart_id,"event "+IntegerToString(index),OBJPROP_COLOR,clrDodgerBlue);}
      if(event[index].country_id==CurrencyToCountryId(currency2)){ObjectSetInteger(chart_id,"event "+IntegerToString(index),OBJPROP_COLOR,clrRed);}
      ObjectCreate(chart_id,"label "+IntegerToString(index),OBJ_TEXT,0,event[index].time+TimeTradeServer()-dt_gmt,SymbolInfoDouble(Symbol(),SYMBOL_BID));
      ObjectSetInteger(chart_id,"label "+IntegerToString(index),OBJPROP_YOFFSET,800);
      ObjectSetInteger(chart_id,"label "+IntegerToString(index),OBJPROP_BACK,true);
      ObjectSetString(chart_id,"label "+IntegerToString(index),OBJPROP_FONT,"Arial");
      ObjectSetInteger(chart_id,"label "+IntegerToString(index),OBJPROP_FONTSIZE,10);
      ObjectSetDouble(chart_id,"label "+IntegerToString(index),OBJPROP_ANGLE,-90);
      if(event[index].country_id==CurrencyToCountryId(currency)){ObjectSetInteger(chart_id,"label "+IntegerToString(index),OBJPROP_COLOR,clrDodgerBlue);}
      if(event[index].country_id==CurrencyToCountryId(currency2)){ObjectSetInteger(chart_id,"label "+IntegerToString(index),OBJPROP_COLOR,clrRed);}
      ObjectSetString(chart_id,"label "+IntegerToString(index),OBJPROP_TEXT,CountryIdToCurrency((ENUM_COUNTRY_ID)event[index].country_id)+" |  "+eventname[index]+"  |  "+(string)event[index].importance);
     }
   return index;
  }  
 

I might have found a slight bug for those with NY Close broker time:

When starting the tester pre-gmt shift then letting the test run over the GMT shift date, it gives different news placement to starting the tester post GMT shift,  so it seems the transition from wintertime to summertime is only working during initialisation. 


Correct : Post GMT shift tester start Incorrect : Pre-GMT shift tester start


looking at the GMT()  function, it assumes that there are Sunday candles (tm.day_of_week==0). My broker has NY close broker time so doesn't have Sundays,  so tm.day_of_week==0 needs changing to tm.day_of_week==1 or tm.day_of_week<=1 or something similar in the following when using NY close brokers:


   // switch to summertime
   if (tm.mon==3 && tm.day>7 && tm.day_of_week<=1 && tm.hour==7+GMT_offset_winter) // second sunday in march, 7h UTC New York=2h local winter time
     {
      summertime=true;
     }
   // switch to wintertime
   if (tm.mon==11 && tm.day<=7 && tm.day_of_week<=1 && tm.hour==7+GMT_offset_summer) // first sunday in november, 7h UTC New York=2h local summer time
     {
      summertime=false;
     }





 
Chris70:

I did some minor changes in the News.mqh file and tested with calendar events of last week so that I could easily compare with Metaquotes' official calendar.

Here is a little convenient code for that purpose (you can use that for checking if the real event times also match if you use your broker (with my broker the time data are correct). One stupid thing is that the values (prev./forecast/actual) are supplied as long variables with a separate value for the digits, instead of simply using double variables. I haven't found out yet how to correctly convert that to doubles. You'll see what I mean if you run this code (as always: with an existing newshistory.bin file or at least once on live data):

and here the News.mqh with the minor changes:

Hi, 

I am getting an Out of range error when testing your code: 

Files:
 
i cant log in to trading area it always says some error occure
 
Julian85:

Hi, 

I am getting an Out of range error when testing your code: 

I think you have to increase the array size to 50,000 instead of 10,000 for those 3 array variable.  The number news event already exist 10,000 and the array is not big enough to hold.

                        CNews(void)
                       {
                        ArrayResize(event,500000,0);ZeroMemory(event);
                        ArrayResize(eventname,500000,0);ZeroMemory(eventname);
                        ArrayResize(future_eventname,500000,0);ZeroMemory(future_eventname);
                        GMT_offset_winter=GMT_OFFSET_WINTER_DEFAULT;
                        GMT_offset_summer=GMT_OFFSET_SUMMER_DEFAULT;
                        last_update=0;
                        SaveHistory(true);
                        LoadHistory(true);
                       }
                    ~CNews(void){};
 

Hi everyone,

I have also the problem with the "out of range error"

Morevover the News Filter doenst even find some news.

Can anyone Help me with this Problem...

Thanks in advance!



 
Do not use this code. It is very poorly written. There is a good library https://www.mql5.com/ru/code/32430 here.
 

Thanks for the information, Enrique!


Unfortunately, I also get an error message.

I used his example "Calendar_Example" EA in the MT5 Backtester platform and got these results:

Can you help me with this?


Reason: