2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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"
|
2026-08-06 00:18:27 +02:00 | | | #include "../Events/EventBus/EventBus.mqh"
|
2026-08-14 16:18:08 +02:00 | | | #include "../Events/Events/NewData.mqh"
|
2026-08-09 07:14:59 +02:00 | | | #include "../Configs/TfBarSeries.mqh"
|
2026-08-04 23:54:09 +02:00 | | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class DataManager
|
| | | {
|
| | | private:
|
2026-08-14 16:18:08 +02:00 | | | bool isSeries;
|
2026-08-04 23:54:09 +02:00 | | | TimeHelpers timeHelper;
|
2026-08-14 16:18:08 +02:00 | | | CArrayList<TfBarSeries*>* rawData;
|
2026-08-10 20:23:58 +02:00 | | | EventBus* m_queue;
|
2026-08-14 16:18:08 +02:00 | | | CArrayList<IndicatorConfig>* indConfigs;
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | void AddTfData(string symbol, ENUM_TIMEFRAMES tradeTf, ENUM_TIMEFRAMES tf, int barsToLoad,
|
2026-08-09 07:14:59 +02:00 | | | ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch);
|
2026-08-06 00:18:27 +02:00 | | | int FindExist(const string symbol, ENUM_TIMEFRAMES tf);
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-04 23:54:09 +02:00 | | |
|
| | | public:
|
2026-08-14 16:18:08 +02:00 | | | DataManager() { rawData = new CArrayList<TfBarSeries*>();}
|
2026-08-06 00:18:27 +02:00 | | | ~DataManager()
|
| | | {
|
| | | for(int i = 0; i < rawData.Size(); i++)
|
| | | {
|
2026-08-09 07:14:59 +02:00 | | | TfBarSeries* s = rawData.At(i);
|
2026-08-14 16:18:08 +02:00 | | | 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
|
| | | }
|
2026-08-10 20:23:58 +02:00 | | | delete s;
|
2026-08-06 00:18:27 +02:00 | | | }
|
| | | }
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | bool CheckAndRelaunch(int index);
|
2026-08-14 16:18:08 +02:00 | | | int Initialize(EventBus* queue, CArrayList<SymbolConfig> &symbolConfigs, CArrayList<IndicatorConfig>* indConfigs, bool _isSeries = true);
|
| | | void Update(int count =3);
|
| | |
|
| | | CArrayList<TfBarSeries*>* GetRawData() { return rawData; }
|
2026-08-04 23:54:09 +02:00 | | | };
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | void DataManager::AddTfData(string symbol, ENUM_TIMEFRAMES tradeTf, ENUM_TIMEFRAMES tf, int barsToLoad,
|
2026-08-09 07:14:59 +02:00 | | | ENUM_TIME_FETCH_TYPE fetchType, int fetchCount, bool relaunch)
|
2026-08-04 23:54:09 +02:00 | | | {
|
| | |
|
2026-08-06 00:18:27 +02:00 | | | if(FindExist(symbol, tf) >= 0)
|
| | | return;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | TfBarSeries* tfSeries = new TfBarSeries();
|
2026-08-06 00:18:27 +02:00 | | | tfSeries.tf = tf;
|
| | | tfSeries.symbol = symbol;
|
2026-08-14 16:18:08 +02:00 | | | tfSeries.tradeTf = tradeTf;
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | tfSeries.bar = new BarSeries();
|
2026-08-14 16:18:08 +02:00 | | | tfSeries.bar.Initialize(symbol, tf, barsToLoad, isSeries);
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | for(int i = 0; i < indConfigs.Size(); i++)
|
| | | {
|
| | | IndicatorConfig c = indConfigs.At(i);
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | // Print(symbol," : ",c.location);
|
| | | IndicatorSeries* indSeries = new IndicatorSeries();
|
| | | indSeries.Initialize(symbol, tf, c.location, c.bufferCount, barsToLoad, c.params, isSeries);
|
| | | tfSeries.AddIndicator(indSeries);
|
| | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | tfSeries.fetchType = fetchType;
|
| | | tfSeries.fetchCount = fetchCount;
|
| | | tfSeries.relaunchOnNewPeriod = relaunch;
|
| | |
|
2026-08-10 20:23:58 +02:00 | | | 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
|
| | | }
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | | rawData.Add(tfSeries);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | int DataManager::FindExist(const string symbol, ENUM_TIMEFRAMES tf)
|
2026-08-06 00:18:27 +02:00 | | | {
|
| | | for(int i = 0; i < rawData.Size(); i++)
|
2026-08-04 23:54:09 +02:00 | | | {
|
2026-08-14 16:18:08 +02:00 | | | TfBarSeries* data = rawData.At(i); // pointer, not value
|
| | | if(data.symbol == symbol && data.tf == tf)
|
2026-08-06 00:18:27 +02:00 | | | return i;
|
| | | }
|
| | | return -1;
|
| | | }
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| Initialize |
|
| | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | int DataManager::Initialize(EventBus* queue, CArrayList<SymbolConfig> &symbolConfigs, CArrayList<IndicatorConfig>* _indConfigs, bool _isSeries = true)
|
2026-08-06 00:18:27 +02:00 | | | {
|
| | | m_queue = queue;
|
2026-08-14 16:18:08 +02:00 | | | indConfigs = _indConfigs;
|
| | | isSeries = _isSeries;
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-10 20:23:58 +02:00 | | | for(int s = 0; s < symbolConfigs.Size(); s++)
|
2026-08-06 00:18:27 +02:00 | | | {
|
2026-08-10 20:23:58 +02:00 | | | SymbolConfig symbolCfg = symbolConfigs.At(s);
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | for(int t = 0; t < symbolCfg.tfConfigs.Size(); t++)
|
2026-08-04 23:54:09 +02:00 | | | {
|
2026-08-06 00:18:27 +02:00 | | | STimeframeConfig config = symbolCfg.tfConfigs.At(t);
|
2026-08-14 16:18:08 +02:00 | | |
|
| | |
|
2026-08-06 00:18:27 +02:00 | | | ENUM_TIMEFRAMES tf = config.tf;
|
| | | int shift = 1;
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | switch(config.fetchType)
|
| | | {
|
| | | case FETCH_FIRST_PREVIOUS_MONTH:
|
2026-08-04 23:54:09 +02:00 | | | {
|
2026-08-06 00:18:27 +02:00 | | | if(config.count < 1)
|
| | | {
|
| | | PrintFormat("Initialization failed: Count must be >= 1 for FETCH_FIRST_PREVIOUS_MONTH (%s, %s)",
|
| | | symbolCfg.symbol, EnumToString(tf));
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | return INIT_FAILED;
|
| | | }
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | shift = Bars(symbolCfg.symbol, tf, timeHelper.FirstOfPreviousMonths(TimeCurrent(), config.count), TimeCurrent());
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | break;
|
2026-08-04 23:54:09 +02:00 | | | }
|
| | |
|
2026-08-06 00:18:27 +02:00 | | | 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());
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | break;
|
| | | }
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | case FETCH_PREVIOUS_DAY:
|
2026-08-04 23:54:09 +02:00 | | | {
|
2026-08-06 00:18:27 +02:00 | | | 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;
|
2026-08-04 23:54:09 +02:00 | | | }
|
| | |
|
| | |
|
2026-08-06 00:18:27 +02:00 | | | default:
|
| | | {
|
| | | if(config.count > 0)
|
| | | shift = config.count;
|
| | |
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
2026-08-14 16:18:08 +02:00 | | | AddTfData(symbolCfg.symbol, symbolCfg.tradeTimeframe, tf, shift,
|
2026-08-09 07:14:59 +02:00 | | | config.fetchType, config.count, config.relaunchOnNewPeriod);
|
| | |
|
2026-08-04 23:54:09 +02:00 | | | }
|
| | | }
|
| | |
|
| | |
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-04 23:54:09 +02:00 | | | return INIT_SUCCEEDED;
|
| | | }
|
2026-08-09 07:14:59 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | void DataManager::Update(int count =3)
|
2026-08-09 07:14:59 +02:00 | | | {
|
2026-08-14 16:18:08 +02:00 | | | for(int i = 0; i < rawData.Size(); i++)
|
2026-08-09 07:14:59 +02:00 | | | {
|
2026-08-14 16:18:08 +02:00 | | | TfBarSeries* series = rawData.At(i); // pointer
|
| | | if(series.Update(count))
|
2026-08-09 07:14:59 +02:00 | | | {
|
2026-08-14 16:18:08 +02:00 | | | NewBarEvent* event = new NewBarEvent(series.symbol, series.tf, series.Time(series.TotalBars() - 1));
|
| | | m_queue.Push(event);
|
2026-08-09 07:14:59 +02:00 | | | }
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool DataManager::CheckAndRelaunch(int index)
|
| | | {
|
2026-08-14 16:18:08 +02:00 | | | if(index < 0 || index >= rawData.Size())
|
| | | return false;
|
| | |
|
| | | TfBarSeries* series = rawData.At(index);
|
| | |
|
| | | if(series == NULL)
|
| | | return false;
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | if(!series.relaunchOnNewPeriod)
|
| | | return false;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | if(TimeCurrent() < series.nextRelaunchTime)
|
| | | return false;
|
| | |
|
| | | int newShift = 1;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | switch(series.fetchType)
|
| | | {
|
| | | case FETCH_FIRST_PREVIOUS_MONTH:
|
2026-08-14 16:18:08 +02:00 | | | newShift = Bars(
|
| | | series.symbol,
|
| | | series.tf,
|
| | | timeHelper.FirstOfPreviousMonths(
|
| | | TimeCurrent(),
|
| | | series.fetchCount
|
| | | ),
|
| | | TimeCurrent()
|
| | | );
|
2026-08-09 07:14:59 +02:00 | | | break;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | case FETCH_PREVIOUS_WEEK:
|
2026-08-14 16:18:08 +02:00 | | | newShift = Bars(
|
| | | series.symbol,
|
| | | series.tf,
|
| | | timeHelper.PreviousWeekStart(
|
| | | TimeCurrent(),
|
| | | series.fetchCount
|
| | | ),
|
| | | TimeCurrent()
|
| | | );
|
2026-08-09 07:14:59 +02:00 | | | break;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | case FETCH_PREVIOUS_DAY:
|
2026-08-14 16:18:08 +02:00 | | | newShift = Bars(
|
| | | series.symbol,
|
| | | series.tf,
|
| | | timeHelper.PreviousDayStart(TimeCurrent()),
|
| | | TimeCurrent()
|
| | | );
|
2026-08-09 07:14:59 +02:00 | | | break;
|
2026-08-14 16:18:08 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | default:
|
| | | return false;
|
| | | }
|
| | |
|
2026-08-14 16:18:08 +02:00 | | | // ---------------------------------------------------------
|
| | | // Destroy old data
|
| | | // ---------------------------------------------------------
|
| | |
|
| | | if(series.bar != NULL)
|
| | | {
|
| | | delete series.bar;
|
| | | series.bar = NULL;
|
| | | }
|
| | |
|
| | | series.ClearIndicators();
|
| | |
|
| | | // ---------------------------------------------------------
|
| | | // Create fresh bar series
|
| | | // ---------------------------------------------------------
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | series.bar = new BarSeries();
|
2026-08-10 20:23:58 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | 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
|
| | | );
|
2026-08-09 07:14:59 +02:00 | | |
|
| | | return true;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|