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 ¶ms[], 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 ¶ms[], 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];
|
| | | }
|
| | | //+------------------------------------------------------------------+
|