//+------------------------------------------------------------------+ //| Atr.mqh| //| Copyright 2026, bandersoft | //| https://www.mql5.com/en/users/fats | //+------------------------------------------------------------------+ //| | //| Average True Range helper. | //| | //+------------------------------------------------------------------+ #ifndef STOPFIRST_ATR_MQH #define STOPFIRST_ATR_MQH //--- GetATRPips reports its result via PriceToPips #include "Symbol.mqh" //+------------------------------------------------------------------+ //| Average True Range on one symbol, timeframe and period | //| | //| Calling iATR, CopyBuffer and IndicatorRelease on every request | //| is expensive once per tick, and a handle created moments earlier | //| usually has no calculated data yet, so CopyBuffer fails and the | //| result is a silent 0 the caller cannot tell from a real reading. | //| | //| The handle is created once and held for the object's lifetime. | //| | //| CAtr atr; | //| int OnInit() { return atr.Init(_Symbol, PERIOD_H1, 14) ? 0 : 1; }| //| void OnTick() { double d = atr.Pips(); } | //+------------------------------------------------------------------+ class CAtr { private: string m_symbol; ENUM_TIMEFRAMES m_timeframe; int m_period; int m_handle; public: CAtr(void); ~CAtr(void); bool Init(const string symbol = "", const ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT, const int period = 14); double Value(const int shift = 0); double Pips(const int shift = 0); void Release(void); bool IsReady(void) const { return m_handle != INVALID_HANDLE; } int Handle(void) const { return m_handle; } }; CAtr::CAtr(void) : m_symbol(_Symbol), m_timeframe(PERIOD_CURRENT), m_period(14), m_handle(INVALID_HANDLE) { } CAtr::~CAtr(void) { Release(); } //+------------------------------------------------------------------+ //| Release the handle, if one is held | //| Returns: nothing. | //+------------------------------------------------------------------+ void CAtr::Release(void) { if(m_handle != INVALID_HANDLE) { IndicatorRelease(m_handle); m_handle = INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| Create the indicator handle | //| Parameters: | //| symbol - empty for the current chart symbol | //| timeframe - PERIOD_CURRENT for the chart timeframe | //| period - ATR averaging period, must be positive | //| | //| Returns: true if the handle was created. Safe to call again to | //| repoint the object; the previous handle is released. | //+------------------------------------------------------------------+ bool CAtr::Init(const string symbol, const ENUM_TIMEFRAMES timeframe, const int period) { Release(); if(period <= 0) return false; m_symbol = (symbol == "") ? _Symbol : symbol; m_timeframe = timeframe; m_period = period; m_handle = iATR(m_symbol, m_timeframe, m_period); return m_handle != INVALID_HANDLE; } //+------------------------------------------------------------------+ //| ATR in price units | //| Parameters: | //| shift - bar to read, 0 being the forming bar | //| | //| Returns: the ATR, or 0 if Init was never called, the handle | //| failed, or the indicator has not calculated yet. A | //| freshly created handle needs a few ticks; retry rather | //| than treating the 0 as a reading. | //+------------------------------------------------------------------+ double CAtr::Value(const int shift) { if(m_handle == INVALID_HANDLE || shift < 0) return 0.0; double buffer[]; ArraySetAsSeries(buffer, true); if(CopyBuffer(m_handle, 0, shift, 1, buffer) <= 0) return 0.0; return buffer[0]; } //+------------------------------------------------------------------+ //| ATR expressed in pips | //| Returns: the ATR in pips, or 0 - see Value(). | //+------------------------------------------------------------------+ double CAtr::Pips(const int shift) { double value = Value(shift); if(value <= 0) return 0.0; return PriceToPips(value, m_symbol); } //+------------------------------------------------------------------+ //| Cache backing the GetATRPips wrapper | //| | //| Parallel primitive arrays rather than an array of CAtr: MQL5 | //| reports undeleted dynamic objects when a program unloads, and a | //| file-scope cache has no destructor to clean up in. Handles are | //| released by the terminal at unload, or explicitly by | //| ReleaseAtrCache(). | //+------------------------------------------------------------------+ string g_atrKeys[]; int g_atrHandles[]; //+------------------------------------------------------------------+ //| Release every handle the GetATRPips cache holds | //| Returns: nothing. Call from OnDeinit if you want them gone early. | //+------------------------------------------------------------------+ void ReleaseAtrCache(void) { int total = ArraySize(g_atrHandles); for(int i = 0; i < total; i++) if(g_atrHandles[i] != INVALID_HANDLE) IndicatorRelease(g_atrHandles[i]); ArrayResize(g_atrKeys, 0); ArrayResize(g_atrHandles, 0); } //+------------------------------------------------------------------+ //| Cached iATR handle for one symbol, timeframe and period | //| Returns: the handle, or INVALID_HANDLE if it could not be made. | //+------------------------------------------------------------------+ int CachedAtrHandle(const string symbol, const ENUM_TIMEFRAMES timeframe, const int period) { string key = symbol + "|" + IntegerToString(timeframe) + "|" + IntegerToString(period); int total = ArraySize(g_atrKeys); for(int i = 0; i < total; i++) if(g_atrKeys[i] == key) return g_atrHandles[i]; int handle = iATR(symbol, timeframe, period); if(handle == INVALID_HANDLE) return INVALID_HANDLE; ArrayResize(g_atrKeys, total + 1); ArrayResize(g_atrHandles, total + 1); g_atrKeys[total] = key; g_atrHandles[total] = handle; return handle; } //+------------------------------------------------------------------+ //| Get ATR value in pips | //| Parameters: | //| period - ATR averaging period | //| timeframe - PERIOD_CURRENT for the chart timeframe | //| symbol - empty for the current chart symbol | //| | //| Kept as a wrapper over the cache so existing callers do not have | //| to hold a CAtr. Prefer CAtr in new code - it makes the handle's | //| lifetime visible. | //| | //| Returns: the ATR in pips, or 0 if the handle could not be made | //| or the indicator has not calculated yet. | //+------------------------------------------------------------------+ double GetATRPips(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT, string symbol = "") { if(symbol == "") symbol = _Symbol; if(period <= 0) return 0; int handle = CachedAtrHandle(symbol, timeframe, period); if(handle == INVALID_HANDLE) return 0; double atr[]; ArraySetAsSeries(atr, true); if(CopyBuffer(handle, 0, 0, 1, atr) <= 0) return 0; return PriceToPips(atr[0], symbol); } #endif // STOPFIRST_ATR_MQH //+------------------------------------------------------------------+