//+------------------------------------------------------------------+ //| Indicator.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" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class Indicator { private: string m_symbol; ENUM_TIMEFRAMES m_tf; int m_handle; MqlParam m_params[]; double m_buffers[]; // flat: buffersCount * barsCount bool RecreateHandle(); bool Initialize(string symbol, ENUM_TIMEFRAMES tf, string name, int handle, int buffersCount, int initialBars, bool asSeries); public: Indicator(); ~Indicator(); int m_barsCount; int m_buffersCount; bool m_asSeries; string m_name; 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); double Value(int bufferIndex, int pShift); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ Indicator::Indicator(): m_handle(INVALID_HANDLE), m_buffersCount(0), m_barsCount(0), m_asSeries(true), m_symbol(""), m_name(""), m_tf(PERIOD_CURRENT) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ Indicator::~Indicator() { if(m_handle != INVALID_HANDLE) { IndicatorRelease(m_handle); m_handle = INVALID_HANDLE; } } //+------------------------------------------------------------------+ bool Indicator::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name, int handle, int buffersCount, int initialBars, bool asSeries) { m_symbol = symbol; m_tf = tf; m_name = name; m_handle = handle; m_buffersCount = buffersCount; m_asSeries = asSeries; ArrayFree(m_params); 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); } //+------------------------------------------------------------------+ //| Overload 2: iCustom without extra parameters | //+------------------------------------------------------------------+ bool Indicator::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name, int buffersCount, int initialBars, bool asSeries) { 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); } //+------------------------------------------------------------------+ //| Overload 3: Initialization with MqlParam array (custom indicator)| //+------------------------------------------------------------------+ bool Indicator::Initialize(string symbol, ENUM_TIMEFRAMES tf, string name, int buffersCount, int initialBars, const MqlParam ¶ms[], bool asSeries) { m_symbol = symbol; m_tf = tf; m_name = name; m_buffersCount = buffersCount; m_asSeries = asSeries; ArrayFree(m_params); int srcSize = ArraySize(params); 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 for(int i = 0; i < srcSize; i++) 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool Indicator::Update(int barsCount = 3) { if(barsCount <= 0 || m_buffersCount <= 0) return false; if(m_handle == INVALID_HANDLE /*&& !RecreateHandle()*/) return false; m_barsCount = barsCount; ArrayResize(m_buffers, m_buffersCount * m_barsCount); // Temporary buffer – its series flag is set to match m_asSeries double tempBuffer[]; ArraySetAsSeries(tempBuffer, m_asSeries); for(int i = 0; i < m_buffersCount; i++) { int copied = CopyBuffer(m_handle, i, 0, m_barsCount, tempBuffer); if(copied != m_barsCount) return false; int offset = i * m_barsCount; for(int j = 0; j < m_barsCount; j++) m_buffers[offset + j] = tempBuffer[j]; } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool Indicator::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); return (m_handle != INVALID_HANDLE); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double Indicator::Value(int bufferIndex, int pShift) { if(bufferIndex < 0 || bufferIndex >= m_buffersCount || pShift < 0 || pShift >= m_barsCount) return 0.0; return m_buffers[(bufferIndex * m_barsCount) + pShift]; } //+------------------------------------------------------------------+