357 lines
11 KiB
MQL5
357 lines
11 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| 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<TfBarSeries*>* rawData;
| |
EventBus* m_queue;
| |
CArrayList<IndicatorConfig>* 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<TfBarSeries*>();}
| |
~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<SymbolConfig> &symbolConfigs, CArrayList<IndicatorConfig>* indConfigs, bool _isSeries = true);
| |
void Update(int count =3);
| |
| |
CArrayList<TfBarSeries*>* 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<SymbolConfig> &symbolConfigs, CArrayList<IndicatorConfig>* _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;
| |
}
| |
//+------------------------------------------------------------------+
|