MQLArticles/Utils/FA/Atr.mqh

545 lines
36 KiB
MQL5
Raw Permalink Normal View History

2025-10-01 12:22:08 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| 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<00>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<EFBFBD>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<EFBFBD>odo 14
inv_period = 1.0 / m_period; // 1/14 para per<EFBFBD>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<typename T>
inline T MinValue(const T val, const T val2)
{
return val > val2 ? val2 : val;
}
//+------------------------------------------------------------------+
template<typename T>
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<EFBFBD> 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 <EFBFBD>ndice espec<EFBFBD>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