//+------------------------------------------------------------------+ //| DataManager.mqh | //| Copyright 2026, Concrete Goonies | //| https://concrete-goonies.web.app | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Concrete Goonies" #property link "https://concrete-goonies.web.app" #property version "1.00" #include "TimeHelpers.mqh" #include "../Events/EventBus/EventBus.mqh" #include "../Configs/EAConfig.mqh" #include "TimeFrameDataSeries.mqh" #include "../Events/Events/NewBarEvent.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class DataManager { private: EventBus* eventBus; CArrayList* rawData; EAConfig configs; TimeHelpers* timeHelper; int FindExist(const string symbol, ENUM_TIMEFRAMES tf); void AddTfData(string symbol, ENUM_TIMEFRAMES tf, int barsToLoad, ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch); int GetCandleCount(TimeFrameDataSeries* series, datetime time); int GetCandleCount(SymbolConfig &symbolCfg, STimeframeConfig &tfConfig); public: DataManager(EventBus* p_eventBus); ~DataManager(); void Update(int count = 3); int Initialize(EAConfig &c); bool CheckAndRelaunch(int index); CArrayList* GetRawData() { return rawData; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ DataManager::DataManager(EventBus* p_eventBus) : eventBus(p_eventBus) { rawData = new CArrayList(); timeHelper = new TimeHelpers(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ DataManager::~DataManager() { for(int s = 0; s < rawData.Size(); s++) delete rawData.At(s); delete rawData; delete timeHelper; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int DataManager::Initialize(EAConfig &c) { configs = c; for(int s = 0; s < configs.symbolConfigs.Size(); s++) { SymbolConfig symbolCfg = configs.symbolConfigs.At(s); for(int t = 0; t < symbolCfg.tfConfigs.Size(); t++) { STimeframeConfig tfConfig = symbolCfg.tfConfigs.At(t); ENUM_TIMEFRAMES tf = tfConfig.tf; int shift = GetCandleCount(symbolCfg, tfConfig); AddTfData(symbolCfg.symbol, tf, shift, tfConfig.fetchType, tfConfig.count, tfConfig.relaunchOnNewPeriod); } } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void DataManager::Update(int count = 3) { for(int i = 0; i < rawData.Size(); i++) { TimeFrameDataSeries* series = rawData.At(i); if(!series.isReset) if(series.Update(count)) { NewBarEvent* event = new NewBarEvent(series.symbol, series.tf, series.Time(!configs.asSeries ? series.TotalBars() - 1 : 0), count); eventBus.Push(event); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool DataManager::CheckAndRelaunch(int index) { if(index < 0 || index >= rawData.Size()) return false; TimeFrameDataSeries* series = rawData.At(index); if(series == NULL) return false; if(!series.relaunchOnNewPeriod) return false; string symbol = series.symbol; ENUM_TIMEFRAMES tf = series.tf; datetime t = iTime(symbol, tf, 0); if(t < series.nextRelaunchTime) return false; int shift = GetCandleCount(series, t); series.isReset = true; series.bar.Initialize(symbol, tf, shift, configs.asSeries); series.Update(shift); /*/for(int i = 0; i < series.indicators.Size(); i++) series.indicators.At(i).Initialize(symbol, tf, series.indicators.At(i).m_name, series.indicators.At(i).m_buffersCount, shift, configs.asSeries); */ series.nextRelaunchTime = timeHelper.NextRelaunchTime(t, series.fetchType, series.fetchCount); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int DataManager::GetCandleCount(TimeFrameDataSeries* series, datetime time) { // Print(EnumToString(series.fetchType)); int newShift = 21; switch(series.fetchType) { case FETCH_FIRST_PREVIOUS_MONTH: newShift = Bars(series.symbol, series.tf, timeHelper.FirstOfPreviousMonths(time, series.fetchCount), time); //Print(timeHelper.PreviousDayStart(time), ", FETCH_FIRST_PREVIOUS_MONTH", time); break; case FETCH_PREVIOUS_WEEK: newShift = Bars(series.symbol, series.tf, timeHelper.PreviousWeekStart(time, series.fetchCount), time); break; case FETCH_PREVIOUS_DAY: newShift = Bars(series.symbol, series.tf, timeHelper.PreviousDayStart(time), time); break; } return newShift; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int DataManager::GetCandleCount(SymbolConfig &symbolCfg, STimeframeConfig &tfConfig) { ENUM_TIMEFRAMES tf = tfConfig.tf; int shift = 1; string symbol = symbolCfg.symbol; datetime t = iTime(symbol, tf, 0); switch(tfConfig.fetchType) { case FETCH_FIRST_PREVIOUS_MONTH: { if(tfConfig.count < 1) { PrintFormat("Initialization failed: Count must be >= 1 for FETCH_FIRST_PREVIOUS_MONTH (%s, %s)", symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars(symbol, tf, timeHelper.FirstOfPreviousMonths(t, tfConfig.count), t); break; } case FETCH_PREVIOUS_WEEK: { if(tfConfig.count < 1) { PrintFormat("Initialization failed: Count must be >= 1 for FETCH_PREVIOUS_WEEK (%s, %s)", symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars(symbol, tf, timeHelper.PreviousWeekStart(t, tfConfig.count), t); break; } case FETCH_PREVIOUS_DAY: { if(tf >= PERIOD_D1) { PrintFormat("Initialization failed: FETCH_PREVIOUS_DAY cannot be used on %s (%s)", symbol, EnumToString(tf)); return INIT_FAILED; } shift = Bars(symbol, tf, timeHelper.PreviousDayStart(t,tfConfig.count), t); break; } default: { if(tfConfig.count > 0) shift = tfConfig.count; break; } } return shift; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void DataManager::AddTfData(string symbol, ENUM_TIMEFRAMES tf, int barsToLoad, ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch) { if(FindExist(symbol, tf) >= 0) return; TimeFrameDataSeries* tfSeries = new TimeFrameDataSeries(); tfSeries.tf = tf; tfSeries.symbol = symbol; tfSeries.bar = new Candles(); tfSeries.bar.Initialize(symbol, tf, barsToLoad, configs.asSeries); for(int i = 0; i < configs.indConfigs.Size(); i++) { IndicatorConfig c = configs.indConfigs.At(i); Indicator* indSeries = new Indicator(); indSeries.Initialize(symbol, tf, c.location, c.bufferCount, barsToLoad, c.params, configs.asSeries); tfSeries.AddIndicator(indSeries); } tfSeries.fetchType = fetchType; tfSeries.fetchCount = fetchCount; tfSeries.relaunchOnNewPeriod = relaunch; if(relaunch) { datetime t = iTime(symbol, tf, 0); tfSeries.nextRelaunchTime = timeHelper.NextRelaunchTime(t, fetchType, fetchCount); if(tfSeries.nextRelaunchTime <= t) { 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++) { TimeFrameDataSeries* data = rawData.At(i); // pointer, not value if(data.symbol == symbol && data.tf == tf) return i; } return -1; } //+------------------------------------------------------------------+