//+------------------------------------------------------------------+ //| DataManager.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "TimeHelpers.mqh" #include "BarSeries.mqh" #include "IndicatorSeries.mqh" #include "../Events/EventBus/EventBus.mqh" #include "../Events/Events/NewData.mqh" #include "../Configs/TfBarSeries.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class DataManager { private: bool isSeries; TimeHelpers timeHelper; CArrayList* rawData; EventBus* m_queue; CArrayList* indConfigs; void AddTfData(string symbol, ENUM_TIMEFRAMES tradeTf, ENUM_TIMEFRAMES tf, int barsToLoad, ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch); int FindExist(const string symbol, ENUM_TIMEFRAMES tf); public: DataManager() { rawData = new CArrayList();} ~DataManager() { for(int i = 0; i < rawData.Size(); i++) { TfBarSeries* s = rawData.At(i); if(s.bar) delete s.bar; if(s.indicators) { for(int c = 0; c < s.IndicatorCount(); c++) delete s.GetIndicator(c); delete s.indicators; // <-- add this } delete s; } } bool CheckAndRelaunch(int index); int Initialize(EventBus* queue, CArrayList &symbolConfigs, CArrayList* indConfigs, bool _isSeries = true); void Update(int count =3); CArrayList* GetRawData() { return rawData; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void DataManager::AddTfData(string symbol, ENUM_TIMEFRAMES tradeTf, ENUM_TIMEFRAMES tf, int barsToLoad, ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch) { if(FindExist(symbol, tf) >= 0) return; TfBarSeries* tfSeries = new TfBarSeries(); tfSeries.tf = tf; tfSeries.symbol = symbol; tfSeries.tradeTf = tradeTf; tfSeries.bar = new BarSeries(); tfSeries.bar.Initialize(symbol, tf, barsToLoad, isSeries); for(int i = 0; i < indConfigs.Size(); i++) { IndicatorConfig c = indConfigs.At(i); // Print(symbol," : ",c.location); IndicatorSeries* indSeries = new IndicatorSeries(); indSeries.Initialize(symbol, tf, c.location, c.bufferCount, barsToLoad, c.params, isSeries); tfSeries.AddIndicator(indSeries); } tfSeries.fetchType = fetchType; tfSeries.fetchCount = fetchCount; tfSeries.relaunchOnNewPeriod = relaunch; if(relaunch) { tfSeries.nextRelaunchTime = timeHelper.NextRelaunchTime(TimeCurrent(), fetchType, fetchCount); if(tfSeries.nextRelaunchTime <= TimeCurrent()) { Print("Error: nextRelaunchTime is not in the future"); tfSeries.relaunchOnNewPeriod = false; // disable to avoid loop } } rawData.Add(tfSeries); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int DataManager::FindExist(const string symbol, ENUM_TIMEFRAMES tf) { for(int i = 0; i < rawData.Size(); i++) { TfBarSeries* data = rawData.At(i); // pointer, not value if(data.symbol == symbol && data.tf == tf) return i; } return -1; } //+------------------------------------------------------------------+ //| Initialize | //+------------------------------------------------------------------+ int DataManager::Initialize(EventBus* queue, CArrayList &symbolConfigs, CArrayList* _indConfigs, bool _isSeries = true) { m_queue = queue; indConfigs = _indConfigs; isSeries = _isSeries; for(int s = 0; s < symbolConfigs.Size(); s++) { SymbolConfig symbolCfg = symbolConfigs.At(s); for(int t = 0; t < symbolCfg.tfConfigs.Size(); t++) { STimeframeConfig config = symbolCfg.tfConfigs.At(t); ENUM_TIMEFRAMES tf = config.tf; int shift = 1; switch(config.fetchType) { case FETCH_FIRST_PREVIOUS_MONTH: { if(config.count < 1) { PrintFormat("Initialization failed: Count must be >= 1 for FETCH_FIRST_PREVIOUS_MONTH (%s, %s)", symbolCfg.symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars(symbolCfg.symbol, tf, timeHelper.FirstOfPreviousMonths(TimeCurrent(), config.count), TimeCurrent()); break; } case FETCH_PREVIOUS_WEEK: { if(config.count < 1) { PrintFormat("Initialization failed: Count must be >= 1 for FETCH_PREVIOUS_WEEK (%s, %s)", symbolCfg.symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars(symbolCfg.symbol, tf, timeHelper.PreviousWeekStart(TimeCurrent(), config.count), TimeCurrent()); break; } case FETCH_PREVIOUS_DAY: { if(tf >= PERIOD_D1) { PrintFormat( "Initialization failed: FETCH_PREVIOUS_DAY cannot be used on %s (%s)", symbolCfg.symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars( symbolCfg.symbol, tf, timeHelper.PreviousDayStart(TimeCurrent()), TimeCurrent()); break; } default: { if(config.count > 0) shift = config.count; break; } } AddTfData(symbolCfg.symbol, symbolCfg.tradeTimeframe, tf, shift, config.fetchType, config.count, config.relaunchOnNewPeriod); } } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void DataManager::Update(int count =3) { for(int i = 0; i < rawData.Size(); i++) { TfBarSeries* series = rawData.At(i); // pointer if(series.Update(count)) { NewBarEvent* event = new NewBarEvent(series.symbol, series.tf, series.Time(series.TotalBars() - 1)); m_queue.Push(event); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool DataManager::CheckAndRelaunch(int index) { if(index < 0 || index >= rawData.Size()) return false; TfBarSeries* series = rawData.At(index); if(series == NULL) return false; if(!series.relaunchOnNewPeriod) return false; if(TimeCurrent() < series.nextRelaunchTime) return false; int newShift = 1; switch(series.fetchType) { case FETCH_FIRST_PREVIOUS_MONTH: newShift = Bars( series.symbol, series.tf, timeHelper.FirstOfPreviousMonths( TimeCurrent(), series.fetchCount ), TimeCurrent() ); break; case FETCH_PREVIOUS_WEEK: newShift = Bars( series.symbol, series.tf, timeHelper.PreviousWeekStart( TimeCurrent(), series.fetchCount ), TimeCurrent() ); break; case FETCH_PREVIOUS_DAY: newShift = Bars( series.symbol, series.tf, timeHelper.PreviousDayStart(TimeCurrent()), TimeCurrent() ); break; default: return false; } // --------------------------------------------------------- // Destroy old data // --------------------------------------------------------- if(series.bar != NULL) { delete series.bar; series.bar = NULL; } series.ClearIndicators(); // --------------------------------------------------------- // Create fresh bar series // --------------------------------------------------------- series.bar = new BarSeries(); if(!series.bar.Initialize( series.symbol, series.tf, newShift,isSeries )) { delete series.bar; series.bar = NULL; return false; } // --------------------------------------------------------- // Create fresh indicators // --------------------------------------------------------- for(int i = 0; i < indConfigs.Size(); i++) { IndicatorConfig c = indConfigs.At(i); IndicatorSeries* indSeries = new IndicatorSeries(); if(!indSeries.Initialize( series.symbol, series.tf, c.location, c.bufferCount, newShift, c.params,isSeries )) { delete indSeries; continue; } series.AddIndicator(indSeries); } // --------------------------------------------------------- // Schedule next relaunch // --------------------------------------------------------- series.nextRelaunchTime = timeHelper.NextRelaunchTime( TimeCurrent(), series.fetchType, series.fetchCount ); return true; } //+------------------------------------------------------------------+