//+------------------------------------------------------------------+ //| CalendarMonitorCached.mq5 | //| Copyright (c) 2022, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright (c) 2022, MetaQuotes Ltd." #property description "Output a table with selected calendar events." #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 #property tester_file "xyz.cal" #define LOGGING #include "..\..\Include\CalendarFilterCached.mqh" #include "..\..\Include\CalendarCache.mqh" #include "..\..\Include\Tableau.mqh" #include "..\..\Include\AutoPtr.mqh" #include "..\..\Include\StringUtils.mqh" //+------------------------------------------------------------------+ //| I N P U T S | //+------------------------------------------------------------------+ 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 string CalendarCacheFile = "xyz.cal"; 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 fptr; AutoPtr t; AutoPtr cache; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { cache = new CalendarCache(CalendarCacheFile, true); if(cache[].isLoaded()) { fptr = new CalendarFilterCached(cache[]); } else { if(MQLInfoInteger(MQL_TESTER)) { Print("Can't run in the tester without calendar cache file"); return INIT_FAILED; } else if(StringLen(CalendarCacheFile)) { Alert("Calendar cache not found, trying to create '" + CalendarCacheFile + "'"); cache = new CalendarCache(); 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_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; } //+------------------------------------------------------------------+