//+------------------------------------------------------------------+
//|                                                HistoryLoader.mqh |
//|                      Copyright 2020, Igor Ryabchikov (aka Rigal) |
//|                     https://tlap.com/forum/profile/109537-rigal/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, Igor Ryabchikov (aka Rigal)"
#property link      "https://tlap.com/forum/profile/109537-rigal/"
#property strict
#define HISTORY_UPDATE_PERIOD_SECONDS 3600

ENUM_TIMEFRAMES TIMEFRAMES[] = {
                  PERIOD_M1,
                  PERIOD_M5,
                  PERIOD_M15,
                  PERIOD_M30,
                  PERIOD_H1,
                  PERIOD_H4,
                  PERIOD_D1,
                  PERIOD_W1, 
                  PERIOD_MN1
                };
class HistoryLoader {
   string symbol;
   bool isHistoryLoaded;
   datetime nextHistoryUpdate;
   
   bool ForceLoadBarsForPeriod(ENUM_TIMEFRAMES _tf) {
      int bars=0;
      for (int i = 0; i < 10; i++) {
         ResetLastError();
         bars = iBars(symbol, _tf);
         if (bars > 0)
            return(true);
         int err = GetLastError();
         if (err != 4066) {
            // ERR_HISTORY_WILL_UPDATED   4066   Requested history data in updating state.
            return(false); 
         } else {
            Print(symbol + ":" + TimePeriodToStr(_tf) + ", attempt " + IntegerToString(i) + ": got 'History in updating state', sleeping 2 seconds before retry");
            Sleep(2000); 
         }
      }
      return (false);   
   }
   bool ForceLoadBars() {
      Print("[Re-]loading historical data for " + symbol);
      ulong microsStart = GetMicrosecondCount();
      for (int i = 0; i < ArraySize(TIMEFRAMES); i++) {
         RefreshRates();
         if(!ForceLoadBarsForPeriod(TIMEFRAMES[i])
         || iClose(symbol, TIMEFRAMES[i], 0) < DBL_EPSILON
         || iOpen(symbol, TIMEFRAMES[i], 0) < DBL_EPSILON
         || iMA(symbol, TIMEFRAMES[i], 1, 0, MODE_SMA, PRICE_MEDIAN, 0) < DBL_EPSILON) {
            Print("Failed to load history for ", symbol, ":", TimePeriodToStr(TIMEFRAMES[i]));
            Alert("Failed to load history for ", symbol, ":", TimePeriodToStr(TIMEFRAMES[i]));
            return (false);
         }
      }
      Print("Successfully [re-]loadede historical data for " + symbol + " (" + IntegerToString((GetMicrosecondCount() - microsStart) / 1000) + " milliseconds)");
      return (true);
   }  
   string TimePeriodToStr(int period){
      switch(period){
         case PERIOD_CURRENT: return ("Current time frame");
         case PERIOD_M1:  return ("M1");
         case PERIOD_M5:  return ("M5");
         case PERIOD_M15: return ("M15");
         case PERIOD_M30: return ("M30");
         case PERIOD_H1:  return ("H1");
         case PERIOD_H4:  return ("H4");
         case PERIOD_D1:  return ("D1");
         case PERIOD_W1:  return ("W1");
         case PERIOD_MN1: return ("MN");
      }
      return ("D1");
   }

public:
   HistoryLoader(string _symbol) {
      symbol = _symbol;
      isHistoryLoaded = ForceLoadBars();
      nextHistoryUpdate = TimeCurrent();
      if(isHistoryLoaded)
         nextHistoryUpdate += int(double(rand()) / 32767 * HISTORY_UPDATE_PERIOD_SECONDS);
   }
   
   bool IsHistoryLoaded() {
      return (isHistoryLoaded);
   }
   
   void CheckReload() {
      if(TimeCurrent() > nextHistoryUpdate) {
         if(ForceLoadBars())
            nextHistoryUpdate += HISTORY_UPDATE_PERIOD_SECONDS;
      }
   }
};