forked from nique_372/MQLArticles
552 lines
37 KiB
MQL5
552 lines
37 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| 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 MQLARTICLES_UTILS_FA_ATR_MQH
| |
#define MQLARTICLES_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[];
| |
ENUM_TIMEFRAMES timeframe;
| |
int handle;
| |
| |
public:
| |
~CAtr();
| |
CAtr();
| |
| |
//---
| |
inline bool IsIdxValid(const int idx, const string& function_name) const;
| |
| |
//---
| |
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);
| |
void Create(int handle_);
| |
| |
//---
| |
inline ENUM_TIMEFRAMES Timeframe() const { return timeframe; }
| |
inline int Handle() const { return handle; }
| |
void Free() { ArrayFree(arr); }
| |
| |
//---
| |
// Para evitar erroers use el check..
| |
double operator[](int _index) const { return arr[_index]; }
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Contructor and Destructor |
| |
//+------------------------------------------------------------------+
| |
CAtr::CAtr(void)
| |
: handle(INVALID_HANDLE), timeframe(WRONG_VALUE)
| |
{
| |
ArrayResize(arr, 0);
| |
}
| |
//+------------------------------------------------------------------+
| |
CAtr::~CAtr()
| |
{
| |
if(handle != INVALID_HANDLE)
| |
IndicatorRelease(handle);
| |
//Free();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Check index |
| |
//+------------------------------------------------------------------+
| |
inline bool CAtr::IsIdxValid(const int idx, const string& function_name) 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()), function_name);
| |
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, (timeframe = 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_;
| |
int16_t atemps = 250;
| |
double dummy[100];
| |
| |
//---
| |
while(atemps-- > 0)
| |
{
| |
//--- Copiamos
| |
if(CopyBuffer(handle, 0, 0, 100, dummy) != 100)
| |
continue;
| |
| |
//--- Verificamos que la data sea valida
| |
if(DataIsValid(dummy))
| |
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);
| |
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<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á 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 // MQLARTICLES_UTILS_FA_ATR_MQH
| |
//+------------------------------------------------------------------+
|