Files
2025-02-07 19:15:50 +03:30

172 lines
12 KiB
Plaintext

//+------------------------------------------------------------------+
//| RingBuffer.mqh |
//| Copyright 2016, Vasiliy Sokolov. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link "http://www.mql5.com"
#include "RiBuffDbl.mqh"
#include "RiBuffInt.mqh"
//+------------------------------------------------------------------+
//| Calculate the exponential moving average in the ring buffer |
//+------------------------------------------------------------------+
class CRiMaxMin : public CRiBuffDbl
{
private:
CRiBuffInt m_max;
CRiBuffInt m_min;
bool m_full;
int m_max_ind;
int m_min_ind;
protected:
virtual void OnAddValue(double value);
virtual void OnCalcValue(int index);
virtual void OnChangeValue(int index, double del_value, double new_value);
virtual void OnSetMaxTotal(int max_total);
public:
CRiMaxMin(void);
int MaxIndex(int max_period = 0);
int MinIndex(int min_period = 0);
double MaxValue(int max_period = 0);
double MinValue(int min_period = 0);
void GetMaxIndexes(int& array[]);
void GetMinIndexes(int& array[]);
};
CRiMaxMin::CRiMaxMin(void)
{
m_full = false;
m_max_ind = 0;
m_min_ind = 0;
}
void CRiMaxMin::GetMaxIndexes(int& array[])
{
m_max.ToArray(array);
}
void CRiMaxMin::GetMinIndexes(int& array[])
{
m_min.ToArray(array);
}
//+------------------------------------------------------------------+
//| Change the size of internal buffers according to the new size |
//| of the main buffer |
//+------------------------------------------------------------------+
void CRiMaxMin::OnSetMaxTotal(int max_total)
{
m_max.SetMaxTotal(max_total);
m_min.SetMaxTotal(max_total);
}
//+------------------------------------------------------------------+
//| Calculate Max/Min indices |
//+------------------------------------------------------------------+
void CRiMaxMin::OnAddValue(double value)
{
m_max_ind--;
m_min_ind--;
int last = GetTotal()-1;
if(m_max_ind > 0 && value >= GetValue(m_max_ind))
m_max_ind = last;
if(m_min_ind > 0 && value <= GetValue(m_min_ind))
m_min_ind = last;
OnCalcValue(last);
}
//+------------------------------------------------------------------+
//| Calculate Max/Min indices |
//+------------------------------------------------------------------+
void CRiMaxMin::OnCalcValue(int index)
{
int max = 0, min = 0;
int offset = m_full ? 1 : 0;
double value = GetValue(index);
int p_ind = index-1;
while(p_ind >= 0 && value >= GetValue(p_ind))
{
int extr = m_max.GetValue(p_ind+offset);
max += extr + 1;
p_ind = GetTotal() - 1 - max - 1;
}
p_ind = GetTotal()-2;
while(p_ind >= 0 && value <= GetValue(p_ind))
{
int extr = m_min.GetValue(p_ind+offset);
min += extr + 1;
p_ind = GetTotal() - 1 - min - 1;
}
m_max.AddValue(max);
m_min.AddValue(min);
if(!m_full && GetTotal() == GetMaxTotal())
m_full = true;
}
//+------------------------------------------------------------------+
//| Recalculate High/Low indices following the changes |
//| of the value by an arbitrary index |
//+------------------------------------------------------------------+
void CRiMaxMin::OnChangeValue(int index, double del_value, double new_value)
{
if(m_max_ind >= 0 && new_value >= GetValue(m_max_ind))
m_max_ind = index;
if(m_min_ind >= 0 && new_value >= GetValue(m_min_ind))
m_min_ind = index;
for(int i = index; i < GetTotal(); i++)
OnCalcValue(i);
}
//+------------------------------------------------------------------+
//| Get the maximum element index |
//+------------------------------------------------------------------+
int CRiMaxMin::MaxIndex(int max_period = 0)
{
int limit = 0;
if(max_period > 0 && max_period <= m_max.GetTotal())
{
m_max_ind = -1;
limit = m_max.GetTotal() - max_period;
}
if(m_max_ind >=0)
return m_max_ind;
int c_max = m_max.GetTotal()-1;
while(c_max > limit)
{
int ext = m_max.GetValue(c_max);
if((c_max - ext) <= limit)
return c_max;
c_max = c_max - ext - 1;
}
return limit;
}
//+------------------------------------------------------------------+
//| Get the minimum element index |
//+------------------------------------------------------------------+
int CRiMaxMin::MinIndex(int min_period = 0)
{
int limit = 0;
if(min_period > 0 && min_period <= m_min.GetTotal())
{
limit = m_min.GetTotal() - min_period;
m_min_ind = -1;
}
if(m_min_ind >=0)
return m_min_ind;
int c_min = m_min.GetTotal()-1;
while(c_min > limit)
{
int ext = m_min.GetValue(c_min);
if((c_min - ext) <= limit)
return c_min;
c_min = c_min - ext - 1;
}
return limit;
}
//+------------------------------------------------------------------+
//| Get the maximum element value |
//+------------------------------------------------------------------+
double CRiMaxMin::MaxValue(int max_period = 0)
{
return GetValue(MaxIndex(max_period));
}
//+------------------------------------------------------------------+
//| Get the minimum element value |
//+------------------------------------------------------------------+
double CRiMaxMin::MinValue(int min_period = 0)
{
return GetValue(MinIndex(min_period));
}