They referred to project documents that are not part of this repository. Comments only; PriceToPips is now documented as not rounding, which is what it does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
217 lines
9 KiB
MQL5
217 lines
9 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|
|
//+------------------------------------------------------------------+
|