545 lines
No EOL
36 KiB
MQL5
545 lines
No EOL
36 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 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<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 |