//+------------------------------------------------------------------+ //| Atr.mqh | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372/news | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372/news" #property strict #ifndef UTILS_FA_ATR_MQH #define UTILS_FA_ATR_MQH //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include "SimpleLogger.mqh" #include "BarControler.mqh" //+------------------------------------------------------------------+ //| Resources | //+------------------------------------------------------------------+ #resource "AtrCts.ex5" //+------------------------------------------------------------------+ //| Class to handle the atr | //+------------------------------------------------------------------+ class CAtr : public CLoggerBase { private: double arr[]; int handle; inline bool IsIdxValid(const int idx) const; public: ~CAtr(); CAtr(); void SetAsSeries(bool flag); inline void CopyData(int start, int count); inline void CopyData(int count); void Create(ENUM_TIMEFRAMES tf, string symb, int period, bool execute_on_bar, bool hide_indicator = true); inline int Handle() const { return handle; } void Create(int handle_); void Free() { ArrayFree(arr); } double operator[](int _index) const { return IsIdxValid(_index) ? arr[_index] : 0.00; } }; //+------------------------------------------------------------------+ //| Contructor and Destructor | //+------------------------------------------------------------------+ CAtr::CAtr(void) : handle(INVALID_HANDLE) { ArrayResize(arr, 0); } //+------------------------------------------------------------------+ CAtr::~CAtr() { if(handle != INVALID_HANDLE) IndicatorRelease(handle); Free(); } //+------------------------------------------------------------------+ //| Check index | //+------------------------------------------------------------------+ inline bool CAtr::IsIdxValid(const int idx) const { if((int)arr.Size() > idx && idx >= 0) return true; else { LogError(StringFormat("Acceso al array de atr denegado, indice %d fuera de rango, tamaño del array = %u", idx, arr.Size()), FUNCION_ACTUAL); return false; } } //+------------------------------------------------------------------+ inline void CAtr::CopyData(int start, int count) { ResetLastError(); if(CopyBuffer(handle, 0, start, count, arr) < count) { LogError(StringFormat("No se pudo copiar data del indicador de atr [start %d -> count: %d], ultimo error = %d", start, count, GetLastError()), FUNCION_ACTUAL); // FastLog(FUNCION_ACTUAL, INFO_TEXT, StringFormat("El handle del indicador es %s", (handle == INVALID_HANDLE ? "invalido" : "valido"))); } } //+------------------------------------------------------------------+ inline void CAtr::CopyData(int count) { ResetLastError(); if(CopyBuffer(handle, 0, 0, count, arr) < count) { LogError(StringFormat("No se pudo copiar data del indicador de atr [0 -> count: %d], ultimo error = %d", count, GetLastError()), FUNCION_ACTUAL); //FastLog(FUNCION_ACTUAL, INFO_TEXT, StringFormat("El handle del indicador es %s", (handle == INVALID_HANDLE ? "invalido" : "valido"))); } } //+------------------------------------------------------------------+ void CAtr::Create(ENUM_TIMEFRAMES tf, string symb, int period, bool execute_on_bar, bool hide_indicator = true) { ResetLastError(); if(hide_indicator) TesterHideIndicators(true); int handle_ = iCustom(symb, tf, "::AtrCts.ex5", period, execute_on_bar); if(hide_indicator) TesterHideIndicators(false); Create(handle_); } //+------------------------------------------------------------------+ void CAtr::Create(int handle_) { if(handle_ == INVALID_HANDLE) { LogFatalError(StringFormat("No se pudo crear el handle para el indicador atr, ultimo error = %d", GetLastError()), FUNCION_ACTUAL); Remover(); return; } this.handle = handle_; int atemps = 1000; double dummy[]; bool is = false; ArrayResize(dummy, 0); ArraySetAsSeries(dummy, true); while(atemps-- > 0) { CopyBuffer(handle, 0, 0, 500, dummy); is = DataIsValid(dummy); if(is) break; } } //+------------------------------------------------------------------+ void CAtr::SetAsSeries(bool flag) { ArraySetAsSeries(this.arr, flag); } //+------------------------------------------------------------------+ bool DataIsValid(const double &arr[]) { for(int i = 0; i < (int)arr.Size(); i++) { if(arr[i] == 0.00 || arr[i] == EMPTY_VALUE) return false; } return true; } //+------------------------------------------------------------------+ //| Base class to implement the atr | //+------------------------------------------------------------------+ class CAtrOptimized : public CLoggerBase { protected: ENUM_TIMEFRAMES m_timeframe; string m_symbol; int m_index; int m_period; double m_atr_value; // Valor ATR específico para m_index double wilder_factor; double inv_period; //--- double high[]; double low[]; double close[]; double atr[]; //--- CBarControler* controler_curr; inline double CalculeTrueRange(int idx); inline double CalculeTrueRangeForIndexCts(int index); bool CopyData(); public: CAtrOptimized(ENUM_TIMEFRAMES tf, string symbol, int index, int period); ~CAtrOptimized(void); virtual inline double GetAtrValue(datetime curr_time) = 0; }; //+------------------------------------------------------------------+ //| Constructor and destructor | //+------------------------------------------------------------------+ CAtrOptimized::CAtrOptimized(ENUM_TIMEFRAMES tf, string symbol, int index, int period) { bool custom = false; if(SymbolExist(symbol, custom) == false) { LogFatalError(StringFormat("El simbolo %s, es invalido", symbol), FUNCION_ACTUAL); Remover(); return; } if(period <= 0) { LogFatalError(StringFormat("El periodo del atr %d, es invalido", period), FUNCION_ACTUAL); Remover(); return; } m_timeframe = tf; m_symbol = symbol; m_index = index; m_period = period; m_atr_value = 0.0; controler_curr = new CBarControler(tf, symbol); wilder_factor = (double)(m_period - 1) / m_period; // 13/14 para período 14 inv_period = 1.0 / m_period; // 1/14 para período 14 ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArrayResize(high, 0); ArrayResize(low, 0); ArrayResize(close, 0); ArrayResize(atr, m_period + 2); } //+------------------------------------------------------------------+ CAtrOptimized::~CAtrOptimized(void) { ArrayFree(high); ArrayFree(low); ArrayFree(close); ArrayFree(atr); if(CheckPointer(controler_curr) == POINTER_DYNAMIC && controler_curr != NULL) delete controler_curr; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define CopyAtrExtraValue 3 #define CopyAtr(p) p*2+CopyAtrExtraValue //--- bool CAtrOptimized::CopyData() { int to_copy = CopyAtr(m_period); if(CopyHigh(this.m_symbol, this.m_timeframe, m_index, to_copy, high) < to_copy) { LogError(StringFormat("Al copiar data de los highs, start = %d - count = %d", m_index, to_copy), FUNCION_ACTUAL); return false; } if(CopyClose(this.m_symbol, this.m_timeframe, m_index, to_copy, close) < to_copy) { LogError(StringFormat("Al copiar data de los closes start = %d - count = %d", m_index, to_copy), FUNCION_ACTUAL); return false; } if(CopyLow(this.m_symbol, this.m_timeframe, m_index, to_copy, low) < to_copy) { LogError(StringFormat("Al copiar data de los lows, start = %d - count = %d", m_index, to_copy), FUNCION_ACTUAL); return false; } return true; } //+------------------------------------------------------------------+ inline double CAtrOptimized::CalculeTrueRange(int idx) { return MaxValue(high[idx], close[idx + 1]) - MinValue(low[idx], close[idx + 1]); } //+------------------------------------------------------------------+ inline double CAtrOptimized::CalculeTrueRangeForIndexCts(int index) { double prev_close = iClose(m_symbol, m_timeframe, index + 1); return MaxValue(iHigh(m_symbol, m_timeframe, index), prev_close) - MinValue(iLow(m_symbol, m_timeframe, index), prev_close); } //+------------------------------------------------------------------+ template inline T MinValue(const T val, const T val2) { return val > val2 ? val2 : val; } //+------------------------------------------------------------------+ template inline T MaxValue(const T val, const T val2) { return val < val2 ? val2 : val; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CAtrOptimizedZero : public CAtrOptimized { private: double CalculateAtrForIndexComplete(); double CalculateAtrForIndexFast(); //--- double cached_prev_atr; double cached_tr_period; public: CAtrOptimizedZero(ENUM_TIMEFRAMES tf, string symbol, int period); inline double GetAtrValue(datetime curr_time) override; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CAtrOptimizedZero::CAtrOptimizedZero(ENUM_TIMEFRAMES tf, string symbol, int period) : CAtrOptimized(tf, symbol, 0, period) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline double CAtrOptimizedZero::GetAtrValue(datetime curr_time) { if(controler_curr.IsNewBar(curr_time)) { if(controler_curr.GetNextPrevBarTime() + controler_curr.PeriodsInSeconds() < curr_time) //hubo suspension return CalculateAtrForIndexComplete(); else return CalculateAtrForIndexFast(); } m_atr_value = cached_prev_atr + (CalculeTrueRangeForIndexCts(0) - cached_tr_period) * inv_period; return m_atr_value; } //+------------------------------------------------------------------+ double CAtrOptimizedZero::CalculateAtrForIndexComplete(void) { if(!CopyData()) return 0.00; double sum = 0; for(int i = 1; i <= m_period; i++) // Empezar desde 1, evitar la barra actual sum += CalculeTrueRange(i); sum *= inv_period; double current_atr = sum; for(int i = m_period - 1; i >= 1; i--) // Desde m_period-1 hacia 1 { double tr = CalculeTrueRange(i); current_atr = current_atr * wilder_factor + tr * inv_period; } cached_prev_atr = current_atr; cached_tr_period = CalculeTrueRange(m_period); // TR que se usará en fast double tr_current = CalculeTrueRange(0); m_atr_value = current_atr + (tr_current - cached_tr_period) * inv_period; return m_atr_value; } //+------------------------------------------------------------------+ double CAtrOptimizedZero::CalculateAtrForIndexFast(void) { this.cached_prev_atr = this.m_atr_value; this.cached_tr_period = CalculeTrueRangeForIndexCts(m_period); // no sumamos m_index por que eso zero m_atr_value = cached_prev_atr + (CalculeTrueRangeForIndexCts(0) - cached_tr_period) * inv_period; return m_atr_value; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CAtrOptimizedNonZero : public CAtrOptimized { double CalculateAtrForIndexComplete(); // Calcular solo para el índice específico double CalculateAtrForIndexFast(); double cached_prev_atr; public: CAtrOptimizedNonZero(ENUM_TIMEFRAMES tf, string symbol, int index, int period); inline double GetAtrValue(datetime curr_time) override; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CAtrOptimizedNonZero::CAtrOptimizedNonZero(ENUM_TIMEFRAMES tf, string symbol, int index, int period) : CAtrOptimized(tf, symbol, index, period) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline double CAtrOptimizedNonZero::GetAtrValue(datetime curr_time) { if(controler_curr.IsNewBar(curr_time)) { if(controler_curr.GetNextPrevBarTime() + controler_curr.PeriodsInSeconds() < curr_time) //hubo suspension return CalculateAtrForIndexComplete(); else return CalculateAtrForIndexFast(); } return this.m_atr_value; } //+------------------------------------------------------------------+ double CAtrOptimizedNonZero::CalculateAtrForIndexFast(void) { this.cached_prev_atr = m_atr_value; double tr_current = CalculeTrueRangeForIndexCts(m_index); double cached_prev_tr_period = CalculeTrueRangeForIndexCts(m_index + m_period); m_atr_value = cached_prev_atr + (tr_current - cached_prev_tr_period) * inv_period; return m_atr_value; } //+------------------------------------------------------------------+ double CAtrOptimizedNonZero::CalculateAtrForIndexComplete(void) { if(!CopyData()) return 0.00; double sum = 0; for(int i = 0; i <= m_period; i++) sum += CalculeTrueRange(i); double initial_atr = sum * inv_period; double current_atr = initial_atr; for(int i = m_period - 1; i >= 0; i--) { double tr = CalculeTrueRange(i); current_atr = current_atr * wilder_factor + tr * inv_period; if(i == 1) // Guardar ATR[1] para el cache cached_prev_atr = current_atr; } m_atr_value = current_atr; // current_atr ahora es ATR[0] return m_atr_value; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CAtrUltraOptimized { private: ENUM_TIMEFRAMES m_timeframe; string m_symbol; int m_index; int m_period; public: CAtrUltraOptimized(); ~CAtrUltraOptimized(); //--- CAtrOptimized* base; //--- void SetVariables(ENUM_TIMEFRAMES tf, string symbol_, int index_, int period_); //--- void Period(int new_value) { this.m_period = new_value; } void Symbol(string new_value) { this.m_symbol = new_value; } void Index(int new_value) { this.m_index = new_value; } void Timeframe(ENUM_TIMEFRAMES new_value) { this.m_timeframe = new_value; } //--- inline int Period() const { return m_period; } inline string Symbol() const { return m_symbol; } inline int Index() const { return m_index; } inline ENUM_TIMEFRAMES Timeframe() const { return m_timeframe; } //--- bool SetInternalPointer(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CAtrUltraOptimized::CAtrUltraOptimized(void) : base(NULL), m_timeframe(::Period()), m_symbol(::Symbol()), m_index(0), m_period(14) { } //+------------------------------------------------------------------+ CAtrUltraOptimized::~CAtrUltraOptimized() { if(::CheckPointer(base) == POINTER_DYNAMIC) delete base; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CAtrUltraOptimized::SetInternalPointer(void) { if(::CheckPointer(base) == POINTER_DYNAMIC) delete base; if(this.m_index == 0) base = new CAtrOptimizedZero(this.m_timeframe, this.m_symbol, this.m_period); else base = new CAtrOptimizedNonZero(this.m_timeframe, this.m_symbol, this.m_index, this.m_period); return (::CheckPointer(base) == POINTER_DYNAMIC); } //+------------------------------------------------------------------+ void CAtrUltraOptimized::SetVariables(ENUM_TIMEFRAMES tf, string symbol_, int index_, int period_) { this.m_timeframe = tf; this.m_symbol = symbol_; this.m_index = index_; this.m_period = period_; } #endif