284 lines
9.6 KiB
MQL5
284 lines
9.6 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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<TimeFrameDataSeries*>* 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<TimeFrameDataSeries*>* GetRawData() { return rawData; }
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| |
| |||
//+------------------------------------------------------------------+
| |||
DataManager::DataManager(EventBus* p_eventBus) : eventBus(p_eventBus)
| |||
{
| |||
rawData = new CArrayList<TimeFrameDataSeries*>();
| |||
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;
| |||
}
| |||
//+------------------------------------------------------------------+
|