CalendarMonitorCachedTZ

Author: Copyright (c) 2022-2024, MetaQuotes Ltd.
Miscellaneous
It issuies visual alerts to the screen
0 Views
0 Downloads
0 Favorites
CalendarMonitorCachedTZ
//+------------------------------------------------------------------+
//|                                      CalendarMonitorCachedTZ.mq5 |
//|                         Copyright (c) 2022-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright (c) 2022-2024, MetaQuotes Ltd."
#property description "Outputs and dynamically updates table with selected calendar events. Optionally saves or loads calendar into/from cached cal-file.\n\n"
#property description "For the cache files adjusts event times in history according to (possibly changing) server time zones in past, syncing them with quotes."
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0
// #property tester_file "?.cal"

// #define LOGGING
#include <MQL5Book/TimeServerDST.mqh> // including before Calendar cache enables timezone fix-up support
#include <MQL5Book/CalendarFilterCached.mqh>
#include <MQL5Book/CalendarCache.mqh>
#include <MQL5Book/Tableau.mqh>
#include <MQL5Book/AutoPtr.mqh>
#include <MQL5Book/StringUtils.mqh>

//+------------------------------------------------------------------+
//| I N P U T S                                                      |
//+------------------------------------------------------------------+
input group "Loading and caching";
input string CalendarCacheFile = ""; // CalendarCacheFile (load or save, empty for online)
input string FixCachedTimesBySymbolHistory = "XAUUSD"; // FixCachedTimesBySymbolHistory (for saving only, empty - as is)

input group "General filters";
input string Context; // Context (country - 2 chars, currency - 3 chars, empty - all)
input ENUM_CALENDAR_SCOPE Scope = SCOPE_WEEK;
input bool UseChartCurrencies = true;

input group "Optional filters";
input ENUM_CALENDAR_EVENT_TYPE_EXT Type = TYPE_ANY;
input ENUM_CALENDAR_EVENT_SECTOR_EXT Sector = SECTOR_ANY;
input ENUM_CALENDAR_EVENT_IMPORTANCE_EXT Importance = IMPORTANCE_MODERATE; // Importance (at least)
input string Text;
input ENUM_CALENDAR_HAS_VALUE HasActual = HAS_ANY;
input ENUM_CALENDAR_HAS_VALUE HasForecast = HAS_ANY;
input ENUM_CALENDAR_HAS_VALUE HasPrevious = HAS_ANY;
input ENUM_CALENDAR_HAS_VALUE HasRevised = HAS_ANY;
input int Limit = 30;

input group "Rendering settings";
input ENUM_BASE_CORNER Corner = CORNER_RIGHT_LOWER;
input int Margins = 8;
input int FontSize = 8;
input string FontName = "Consolas";
input color BackgroundColor = clrSilver;
input uchar BackgroundTransparency = 128;    // BackgroundTransparency (255 - opaque, 0 - glassy)

//+------------------------------------------------------------------+
//| G L O B A L S                                                    |
//+------------------------------------------------------------------+
AutoPtr<CalendarFilter> fptr;
AutoPtr<Tableau> t;
AutoPtr<CalendarCache> cache;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   cache = new CalendarCache(CalendarCacheFile, true);
   if(cache[].isLoaded())
   {
      fptr = new CalendarFilterCached(cache[]);
   }
   else
   {
      if(MQLInfoInteger(MQL_TESTER))
      {
         Alert("Can't run in the tester without calendar cache file");
         // return INIT_FAILED; // commented only to pass the automatic validation of the codebase!
         return INIT_SUCCEEDED;
      }
      else
      if(StringLen(CalendarCacheFile))
      {
         Alert("Calendar cache not found, trying to create '" + CalendarCacheFile + "'");
         cache = new CalendarCache();

         if(StringLen(FixCachedTimesBySymbolHistory))
            cache[].adjustTZonHistory(FixCachedTimesBySymbolHistory, true);
         
         if(cache[].save(CalendarCacheFile))
         {
            Alert("File saved. Re-run indicator in online chart or in the tester");
         }
         else
         {
            Alert("Error: ", _LastError);
         }
         ChartIndicatorDelete(0, 0, MQLInfoString(MQL_PROGRAM_NAME));
         return INIT_PARAMETERS_INCORRECT;
      }
      Alert("Currently working in online mode (no cache)");
      fptr = new CalendarFilter(Context);
   }
   CalendarFilter *f = fptr[];
   
   if(!f.isLoaded()) return INIT_FAILED;
   
   if(UseChartCurrencies)
   {
      const string base = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE);
      const string profit = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT);
      f.let(base);
      if(base != profit)
      {
         f.let(profit);
      }
   }
   
   if(Type != TYPE_ANY)
   {
      f.let((ENUM_CALENDAR_EVENT_TYPE)Type);
   }
   
   if(Sector != SECTOR_ANY)
   {
      f.let((ENUM_CALENDAR_EVENT_SECTOR)Sector);
   }
   
   if(Importance != IMPORTANCE_ANY)
   {
      f.let((ENUM_CALENDAR_EVENT_IMPORTANCE)(Importance - 1), GREATER);
   }

   if(StringLen(Text))
   {
      f.let(Text);
   }
   
   if(HasActual != HAS_ANY)
   {
      f.let(LONG_MIN, CALENDAR_PROPERTY_RECORD_ACTUAL, HasActual == HAS_SET ? NOT_EQUAL : EQUAL);
   }

   if(HasPrevious != HAS_ANY)
   {
      f.let(LONG_MIN, CALENDAR_PROPERTY_RECORD_PREVIOUS, HasPrevious == HAS_SET ? NOT_EQUAL : EQUAL);
   }
   
   if(HasRevised != HAS_ANY)
   {
      f.let(LONG_MIN, CALENDAR_PROPERTY_RECORD_REVISED, HasRevised == HAS_SET ? NOT_EQUAL : EQUAL);
   }
   
   if(HasForecast != HAS_ANY)
   {
      f.let(LONG_MIN, CALENDAR_PROPERTY_RECORD_FORECAST, HasForecast == HAS_SET ? NOT_EQUAL : EQUAL);
   }
   
   EventSetTimer(1);
   
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Timer event handler (main processing of calendar goes here)      |
//+------------------------------------------------------------------+
void OnTimer()
{
   CalendarFilter *f = fptr[];

   static const ENUM_CALENDAR_PROPERTY props[] =
   {
      CALENDAR_PROPERTY_RECORD_ID,
      CALENDAR_PROPERTY_RECORD_TIME,
      CALENDAR_PROPERTY_COUNTRY_CURRENCY,
      CALENDAR_PROPERTY_EVENT_NAME,
      CALENDAR_PROPERTY_EVENT_IMPORTANCE,
      CALENDAR_PROPERTY_RECORD_ACTUAL,
      CALENDAR_PROPERTY_RECORD_FORECAST,
      CALENDAR_PROPERTY_RECORD_PREVISED,
      CALENDAR_PROPERTY_RECORD_IMPACT,
      CALENDAR_PROPERTY_EVENT_SECTOR,
   };
   static const int p = ArraySize(props);

   MqlCalendarValue records[];

   f.let(TimeTradeServer() - Scope, TimeTradeServer() + Scope);
   
   const ulong trackID = f.getChangeID();
   if(trackID) // already has a state, try to detect changes
   {
      if(f.update(records)) // find changes that match filters
      {
         // notify user about new changes
         string result[];
         f.format(records, props, result);
         for(int i = 0; i < ArraySize(result) / p; ++i)
         {
            Alert(SubArrayCombine(result, " | ", i * p, p));
         }
         // fall through to the table redraw
      }
      else if(trackID == f.getChangeID())
      {
         return; // no changes in the calendar
      }
   }

   // request complete set of events according to filters
   f.select(records, true, Limit);
   
   // rebuild the table displayed on chart
   string result[];
   f.format(records, props, result, true, true);

   /*
   // on-chart table copy in the log
   for(int i = 0; i < ArraySize(result) / p; ++i)
   {
      Print(SubArrayCombine(result, " | ", i * p, p));
   }
   */

   if(t[] == NULL || t[].getRows() != ArraySize(records) + 1)
   {
      t = new Tableau("CALT", ArraySize(records) + 1, p,
         TBL_CELL_HEIGHT_AUTO, TBL_CELL_WIDTH_AUTO,
         Corner, Margins, FontSize, FontName, FontName + " Bold",
         TBL_FLAG_ROW_0_HEADER,
         BackgroundColor, BackgroundTransparency);
   }
   const string hints[] = {};
   t[].fill(result, hints);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function (dummy here)                 |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   return rates_total;
}

//+------------------------------------------------------------------+

Comments