mql5/Experts/_Architecture/Market/IndicatorSeries.mqh

215 lines
8 KiB
MQL5

2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
//| IndicatorSeries.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
2026-08-06 00:18:27 +02:00
//| https://mql5.com |
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
2026-08-06 00:18:27 +02:00
#property link "https://mql5.com"
2026-08-14 16:18:08 +02:00
#property version "1.10"
2026-08-04 23:54:09 +02:00
class IndicatorSeries
2026-08-14 16:18:08 +02:00
{
2026-08-04 23:54:09 +02:00
private:
2026-08-06 00:18:27 +02:00
string m_symbol;
string m_name;
ENUM_TIMEFRAMES m_tf;
int m_handle;
int m_buffersCount;
int m_barsCount;
2026-08-14 16:18:08 +02:00
bool m_asSeries; // true = shift 0 is the most recent bar
2026-08-06 00:18:27 +02:00
MqlParam m_params[];
2026-08-14 16:18:08 +02:00
double m_buffers[]; // flat: buffersCount * barsCount
2026-08-06 00:18:27 +02:00
bool RecreateHandle();
2026-08-04 23:54:09 +02:00
public:
2026-08-06 00:18:27 +02:00
IndicatorSeries();
~IndicatorSeries();
2026-08-14 16:18:08 +02:00
// Initialisation overloads
bool Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int handle, int buffersCount, int initialBars, bool asSeries = true);
bool Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int buffersCount, int initialBars, bool asSeries = true);
bool Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int buffersCount, int initialBars, const MqlParam &params[], bool asSeries = true);
bool Update(int barsCount = 3);
2026-08-06 00:18:27 +02:00
double Value(int bufferIndex, int pShift);
2026-08-14 16:18:08 +02:00
2026-08-06 00:18:27 +02:00
int Handle() const { return m_handle; }
int BuffersCount() const { return m_buffersCount; }
int Bars() const { return m_barsCount; }
2026-08-14 16:18:08 +02:00
};
2026-08-06 00:18:27 +02:00
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
2026-08-06 00:18:27 +02:00
//| Constructor |
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
IndicatorSeries::IndicatorSeries() : m_handle(INVALID_HANDLE),
m_buffersCount(0),
2026-08-06 00:18:27 +02:00
m_barsCount(0),
2026-08-14 16:18:08 +02:00
m_asSeries(true),
2026-08-06 00:18:27 +02:00
m_symbol(""),
m_name(""),
m_tf(PERIOD_CURRENT)
{
}
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
2026-08-06 00:18:27 +02:00
//| Destructor |
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
IndicatorSeries::~IndicatorSeries()
2026-08-06 00:18:27 +02:00
{
if(m_handle != INVALID_HANDLE)
2026-08-14 16:18:08 +02:00
{
2026-08-06 00:18:27 +02:00
IndicatorRelease(m_handle);
m_handle = INVALID_HANDLE;
2026-08-14 16:18:08 +02:00
}
2026-08-06 00:18:27 +02:00
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Recreate the indicator handle |
2026-08-06 00:18:27 +02:00
//+------------------------------------------------------------------+
bool IndicatorSeries::RecreateHandle()
{
if(ArraySize(m_params) > 0)
m_handle = IndicatorCreate(m_symbol, m_tf, IND_CUSTOM, ArraySize(m_params), m_params);
else
m_handle = iCustom(m_symbol, m_tf, m_name);
2026-08-14 16:18:08 +02:00
2026-08-06 00:18:27 +02:00
return (m_handle != INVALID_HANDLE);
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Overload 1: Initialization using an existing handle |
2026-08-06 00:18:27 +02:00
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
bool IndicatorSeries::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int handle, int buffersCount, int initialBars, bool asSeries)
2026-08-06 00:18:27 +02:00
{
2026-08-14 16:18:08 +02:00
m_symbol = symbol;
m_tf = tf;
m_name = name;
m_handle = handle;
2026-08-06 00:18:27 +02:00
m_buffersCount = buffersCount;
2026-08-14 16:18:08 +02:00
m_asSeries = asSeries;
2026-08-06 00:18:27 +02:00
ArrayFree(m_params);
2026-08-14 16:18:08 +02:00
if(m_handle == INVALID_HANDLE)
{
PrintFormat("IndicatorSeries::Initialize failed: invalid handle | %s %s | %s",
m_symbol, EnumToString(m_tf), m_name);
return false;
}
return Update(initialBars);
2026-08-06 00:18:27 +02:00
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Overload 2: iCustom without extra parameters |
2026-08-06 00:18:27 +02:00
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
bool IndicatorSeries::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int buffersCount, int initialBars, bool asSeries)
2026-08-06 00:18:27 +02:00
{
2026-08-14 16:18:08 +02:00
int handle = iCustom(symbol, tf, name);
if(handle == INVALID_HANDLE)
{
PrintFormat("IndicatorSeries::Initialize failed: iCustom | %s %s | %s | Error=%d",
symbol, EnumToString(tf), name, GetLastError());
return false;
}
return Initialize(symbol, tf, name, handle, buffersCount, initialBars, asSeries);
2026-08-06 00:18:27 +02:00
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Overload 3: Initialization with MqlParam array (custom indicator)|
2026-08-06 00:18:27 +02:00
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
bool IndicatorSeries::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name,
int buffersCount, int initialBars, const MqlParam &params[], bool asSeries)
2026-08-06 00:18:27 +02:00
{
2026-08-14 16:18:08 +02:00
m_symbol = symbol;
m_tf = tf;
m_name = name;
2026-08-06 00:18:27 +02:00
m_buffersCount = buffersCount;
2026-08-14 16:18:08 +02:00
m_asSeries = asSeries;
ArrayFree(m_params);
2026-08-06 00:18:27 +02:00
int srcSize = ArraySize(params);
2026-08-14 16:18:08 +02:00
if(ArrayResize(m_params, srcSize + 1) != srcSize + 1)
return false;
// First parameter = custom indicator name (required for IndicatorCreate)
m_params[0].type = TYPE_STRING;
m_params[0].string_value = m_name;
// Copy the remaining parameters
2026-08-06 00:18:27 +02:00
for(int i = 0; i < srcSize; i++)
2026-08-14 16:18:08 +02:00
m_params[i + 1] = params[i];
if(!RecreateHandle())
{
PrintFormat("IndicatorSeries::Initialize failed: RecreateHandle | %s %s | %s",
m_symbol, EnumToString(m_tf), m_name);
return false;
}
return Update(initialBars);
2026-08-06 00:18:27 +02:00
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Update internal buffers |
2026-08-06 00:18:27 +02:00
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
bool IndicatorSeries::Update(int barsCount = 3)
2026-08-06 00:18:27 +02:00
{
if(barsCount <= 0 || m_buffersCount <= 0)
return false;
2026-08-14 16:18:08 +02:00
if(m_handle == INVALID_HANDLE && !RecreateHandle())
2026-08-06 00:18:27 +02:00
return false;
m_barsCount = barsCount;
ArrayResize(m_buffers, m_buffersCount * m_barsCount);
2026-08-14 16:18:08 +02:00
// Temporary buffer – its series flag is set to match m_asSeries
2026-08-06 00:18:27 +02:00
double tempBuffer[];
2026-08-14 16:18:08 +02:00
ArraySetAsSeries(tempBuffer, m_asSeries);
2026-08-06 00:18:27 +02:00
for(int i = 0; i < m_buffersCount; i++)
2026-08-14 16:18:08 +02:00
{
// CopyBuffer respects the series flag of tempBuffer.
// If m_asSeries == true → tempBuffer[0] = latest bar.
// If m_asSeries == false → tempBuffer[0] = oldest bar.
2026-08-06 00:18:27 +02:00
int copied = CopyBuffer(m_handle, i, 0, m_barsCount, tempBuffer);
if(copied != m_barsCount)
return false;
2026-08-14 16:18:08 +02:00
// Append the segment into the flat m_buffers array.
// We must copy element by element to preserve the series ordering.
int offset = i * m_barsCount;
for(int j = 0; j < m_barsCount; j++)
m_buffers[offset + j] = tempBuffer[j];
}
2026-08-06 00:18:27 +02:00
return true;
}
//+------------------------------------------------------------------+
2026-08-14 16:18:08 +02:00
//| Get a value at a specific buffer and shift |
2026-08-04 23:54:09 +02:00
//+------------------------------------------------------------------+
2026-08-06 00:18:27 +02:00
double IndicatorSeries::Value(int bufferIndex, int pShift)
{
2026-08-14 16:18:08 +02:00
if(bufferIndex < 0 || bufferIndex >= m_buffersCount ||
pShift < 0 || pShift >= m_barsCount)
2026-08-06 00:18:27 +02:00
return 0.0;
2026-08-14 16:18:08 +02:00
// Because we stored the buffer segment exactly as it was copied
// (with tempBuffer's series flag already applied), pShift works uniformly:
// asSeries = true → pShift 0 = latest bar
// asSeries = false → pShift 0 = oldest bar
2026-08-06 00:18:27 +02:00
return m_buffers[(bufferIndex * m_barsCount) + pShift];
}
//+------------------------------------------------------------------+