| //+------------------------------------------------------------------+
|
| //| Indicator.mqh |
|
| //| Copyright 2000-2025, MetaQuotes Ltd. |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| #include "Series.mqh"
|
| //+------------------------------------------------------------------+
|
| //| Class CIndicatorBuffer. |
|
| //| Purpose: Class for access to data of buffers of |
|
| //| technical indicators. |
|
| //| Derives from class CDoubleBuffer. |
|
| //+------------------------------------------------------------------+
|
| class CIndicatorBuffer : public CDoubleBuffer
|
| {
|
| protected:
|
| int m_offset; // shift along the time axis (in bars)
|
| string m_name; // name of buffer
|
|
|
| public:
|
| CIndicatorBuffer(void);
|
| ~CIndicatorBuffer(void);
|
| //--- methods of access to protected data
|
| int Offset(void) const { return(m_offset); }
|
| void Offset(const int offset) { m_offset=offset; }
|
| string Name(void) const { return(m_name); }
|
| void Name(const string name) { m_name=name; }
|
| //--- methods of access to data
|
| double At(const int index) const;
|
| //--- method of refreshing of data in buffer
|
| bool Refresh(const int handle,const int num);
|
| bool RefreshCurrent(const int handle,const int num);
|
|
|
| private:
|
| virtual bool Refresh(void) { return(CDoubleBuffer::Refresh()); }
|
| virtual bool RefreshCurrent(void) { return(CDoubleBuffer::RefreshCurrent()); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor |
|
| //+------------------------------------------------------------------+
|
| CIndicatorBuffer::CIndicatorBuffer(void) : m_offset(0),
|
| m_name("")
|
| {
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CIndicatorBuffer::~CIndicatorBuffer(void)
|
| {
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Access to data in a specified position |
|
| //+------------------------------------------------------------------+
|
| double CIndicatorBuffer::At(const int index) const
|
| {
|
| return(CDoubleBuffer::At(index+m_offset));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Refreshing of data in buffer |
|
| //+------------------------------------------------------------------+
|
| bool CIndicatorBuffer::Refresh(const int handle,const int num)
|
| {
|
| //--- check
|
| if(handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(false);
|
| }
|
| //---
|
| m_data_total=CopyBuffer(handle,num,-m_offset,m_size,m_data);
|
| //---
|
| return(m_data_total>0);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Refreshing of the data in buffer |
|
| //+------------------------------------------------------------------+
|
| bool CIndicatorBuffer::RefreshCurrent(const int handle,const int num)
|
| {
|
| double array[1];
|
| //--- check
|
| if(handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(false);
|
| }
|
| //---
|
| if(CopyBuffer(handle,num,-m_offset,1,array)>0 && m_data_total>0)
|
| {
|
| m_data[0]=array[0];
|
| return(true);
|
| }
|
| //--- error
|
| return(false);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Class CIndicator. |
|
| //| Purpose: Base class of technical indicators. |
|
| //| Derives from class CSeries. |
|
| //+------------------------------------------------------------------+
|
| class CIndicator : public CSeries
|
| {
|
| protected:
|
| int m_handle; // indicator handle
|
| string m_status; // status of creation
|
| bool m_full_release; // flag
|
| bool m_redrawer; // flag
|
|
|
| public:
|
| CIndicator(void);
|
| ~CIndicator(void);
|
| //--- methods of access to protected data
|
| int Handle(void) const { return(m_handle); }
|
| string Status(void) const { return(m_status); }
|
| void FullRelease(const bool flag=true) { m_full_release=flag; }
|
| void Redrawer(const bool flag=true) { m_redrawer=flag; }
|
| //--- method for creating
|
| bool Create(const string symbol,const ENUM_TIMEFRAMES period,
|
| const ENUM_INDICATOR type,const int num_params,const MqlParam ¶ms[]);
|
| virtual bool BufferResize(const int size);
|
| //--- methods of access to data
|
| int BarsCalculated(void) const;
|
| double GetData(const int buffer_num,const int index) const;
|
| int GetData(const int start_pos,const int count,const int buffer_num,double &buffer[]) const;
|
| int GetData(const datetime start_time,const int count,const int buffer_num,double &buffer[]) const;
|
| int GetData(const datetime start_time,const datetime stop_time,const int buffer_num,double &buffer[]) const;
|
| //--- methods for find extremum
|
| int Minimum(const int buffer_num,const int start,const int count) const;
|
| double MinValue(const int buffer_num,const int start,const int count,int &index) const;
|
| int Maximum(const int buffer_num,const int start,const int count) const;
|
| double MaxValue(const int buffer_num,const int start,const int count,int &index) const;
|
| //--- method of "freshening" of the data
|
| virtual void Refresh(const int flags=OBJ_ALL_PERIODS);
|
| //--- methods for working with chart
|
| bool AddToChart(const long chart,const int subwin);
|
| bool DeleteFromChart(const long chart,const int subwin);
|
| //--- methods of conversion of constants to strings
|
| static string MethodDescription(const int val);
|
| static string PriceDescription(const int val);
|
| static string VolumeDescription(const int val);
|
|
|
| protected:
|
| //--- methods of tuning
|
| bool CreateBuffers(const string symbol,const ENUM_TIMEFRAMES period,const int buffers);
|
| virtual bool Initialize(const string symbol,const ENUM_TIMEFRAMES period,
|
| const int num_params,const MqlParam ¶ms[]) {return(false);}
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor |
|
| //+------------------------------------------------------------------+
|
| void CIndicator::CIndicator(void) : m_handle(INVALID_HANDLE),
|
| m_status(""),
|
| m_full_release(false),
|
| m_redrawer(false)
|
| {
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| void CIndicator::~CIndicator(void)
|
| {
|
| //--- indicator handle release
|
| if(m_full_release && m_handle!=INVALID_HANDLE)
|
| {
|
| IndicatorRelease(m_handle);
|
| m_handle=INVALID_HANDLE;
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Creation of the indicator with universal parameters |
|
| //+------------------------------------------------------------------+
|
| bool CIndicator::Create(const string symbol,const ENUM_TIMEFRAMES period,
|
| const ENUM_INDICATOR type,const int num_params,const MqlParam ¶ms[])
|
| {
|
| //--- check history
|
| if(!SetSymbolPeriod(symbol,period))
|
| return(false);
|
| //--- create
|
| m_handle=IndicatorCreate(symbol,period,type,num_params,params);
|
| //--- check result
|
| if(m_handle==INVALID_HANDLE)
|
| return(false);
|
| //--- idicator successfully created
|
| if(!Initialize(symbol,period,num_params,params))
|
| {
|
| //--- initialization failed
|
| IndicatorRelease(m_handle);
|
| m_handle=INVALID_HANDLE;
|
| return(false);
|
| }
|
| //--- ok
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Returns the amount of calculated indicator data |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::BarsCalculated(void) const
|
| {
|
| if(m_handle==INVALID_HANDLE)
|
| return(-1);
|
| //---
|
| return(::BarsCalculated(m_handle));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| API access method "Copying an element of indicator buffer |
|
| //| by specifying number of buffer and position of element" |
|
| //+------------------------------------------------------------------+
|
| double CIndicator::GetData(const int buffer_num,const int index) const
|
| {
|
| CIndicatorBuffer *buffer=At(buffer_num);
|
| //--- check
|
| if(buffer==NULL)
|
| {
|
| Print(__FUNCTION__,": invalid buffer");
|
| return(EMPTY_VALUE);
|
| }
|
| //---
|
| return(buffer.At(index));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| API access method "Copying the buffer of indicator by specifying |
|
| //| a start position and number of elements" |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::GetData(const int start_pos,const int count,const int buffer_num,double &buffer[]) const
|
| {
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(-1);
|
| }
|
| if(buffer_num>=m_buffers_total)
|
| {
|
| SetUserError(ERR_USER_INVALID_BUFF_NUM);
|
| return(-1);
|
| }
|
| //---
|
| return(CopyBuffer(m_handle,buffer_num,start_pos,count,buffer));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| API access method "Copying the buffer of indicator by specifying |
|
| //| start time and number of elements" |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::GetData(const datetime start_time,const int count,const int buffer_num,double &buffer[]) const
|
| {
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(-1);
|
| }
|
| if(buffer_num>=m_buffers_total)
|
| {
|
| SetUserError(ERR_USER_INVALID_BUFF_NUM);
|
| return(-1);
|
| }
|
| //---
|
| return(CopyBuffer(m_handle,buffer_num,start_time,count,buffer));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| API access method "Copying the buffer of indicator by specifying |
|
| //| start and final time |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::GetData(const datetime start_time,const datetime stop_time,const int buffer_num,double &buffer[]) const
|
| {
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(-1);
|
| }
|
| if(buffer_num>=m_buffers_total)
|
| {
|
| SetUserError(ERR_USER_INVALID_BUFF_NUM);
|
| return(-1);
|
| }
|
| //---
|
| return(CopyBuffer(m_handle,buffer_num,start_time,stop_time,buffer));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Find minimum of a specified buffer |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::Minimum(const int buffer_num,const int start,const int count) const
|
| {
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(-1);
|
| }
|
| if(buffer_num>=m_buffers_total)
|
| {
|
| SetUserError(ERR_USER_INVALID_BUFF_NUM);
|
| return(-1);
|
| }
|
| //---
|
| CIndicatorBuffer *buffer=At(buffer_num);
|
| if(buffer==NULL)
|
| return(-1);
|
| //---
|
| return(buffer.Minimum(start,count));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Find minimum of a specified buffer |
|
| //+------------------------------------------------------------------+
|
| double CIndicator::MinValue(const int buffer_num,const int start,const int count,int &index) const
|
| {
|
| int idx=Minimum(buffer_num,start,count);
|
| double res=EMPTY_VALUE;
|
| //--- check
|
| if(idx!=-1)
|
| {
|
| CIndicatorBuffer *buffer=At(buffer_num);
|
| res=buffer.At(idx);
|
| index=idx;
|
| }
|
| //---
|
| return(res);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Find maximum of a specified buffer |
|
| //+------------------------------------------------------------------+
|
| int CIndicator::Maximum(const int buffer_num,const int start,const int count) const
|
| {
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(-1);
|
| }
|
| if(buffer_num>=m_buffers_total)
|
| {
|
| SetUserError(ERR_USER_INVALID_BUFF_NUM);
|
| return(-1);
|
| }
|
| //---
|
| CIndicatorBuffer *buffer=At(buffer_num);
|
| if(buffer==NULL)
|
| return(-1);
|
| //---
|
| return(buffer.Maximum(start,count));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Find maximum of specified buffer |
|
| //+------------------------------------------------------------------+
|
| double CIndicator::MaxValue(const int buffer_num,const int start,const int count,int &index) const
|
| {
|
| int idx=Maximum(buffer_num,start,count);
|
| double res=EMPTY_VALUE;
|
| //--- check
|
| if(idx!=-1)
|
| {
|
| CIndicatorBuffer *buffer=At(buffer_num);
|
| res=buffer.At(idx);
|
| index=idx;
|
| }
|
| //---
|
| return(res);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Creating data buffers of indicator |
|
| //+------------------------------------------------------------------+
|
| bool CIndicator::CreateBuffers(const string symbol,const ENUM_TIMEFRAMES period,const int buffers)
|
| {
|
| bool result=true;
|
| //--- check
|
| if(m_handle==INVALID_HANDLE)
|
| {
|
| SetUserError(ERR_USER_INVALID_HANDLE);
|
| return(false);
|
| }
|
| if(buffers==0)
|
| return(false);
|
| if(!Reserve(buffers))
|
| return(false);
|
| //---
|
| for(int i=0;i<buffers;i++)
|
| result&=Add(new CIndicatorBuffer);
|
| //---
|
| if(result)
|
| m_buffers_total=buffers;
|
| //---
|
| return(result);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Set size of buffers of indicator |
|
| //+------------------------------------------------------------------+
|
| bool CIndicator::BufferResize(const int size)
|
| {
|
| if(size>m_buffer_size && !CSeries::BufferResize(size))
|
| return(false);
|
| //-- history is avalible
|
| int total=Total();
|
| for(int i=0;i<total;i++)
|
| {
|
| CIndicatorBuffer *buff=At(i);
|
| //--- check pointer
|
| if(buff==NULL)
|
| return(false);
|
| buff.Size(size);
|
| }
|
| //--- ok
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Refreshing data of indicator |
|
| //+------------------------------------------------------------------+
|
| void CIndicator::Refresh(const int flags)
|
| {
|
| int i;
|
| CIndicatorBuffer *buff;
|
| //--- refreshing buffers
|
| for(i=0;i<Total();i++)
|
| {
|
| buff=At(i);
|
| if(m_redrawer)
|
| {
|
| buff.Refresh(m_handle,i);
|
| continue;
|
| }
|
| if(!(flags&m_timeframe_flags))
|
| {
|
| if(m_refresh_current)
|
| buff.RefreshCurrent(m_handle,i);
|
| }
|
| else
|
| buff.Refresh(m_handle,i);
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Adds indicator to chart |
|
| //+------------------------------------------------------------------+
|
| bool CIndicator::AddToChart(const long chart,const int subwin)
|
| {
|
| if(ChartIndicatorAdd(chart,subwin,m_handle))
|
| {
|
| m_name=ChartIndicatorName(chart,subwin,ChartIndicatorsTotal(chart,subwin)-1);
|
| return(true);
|
| }
|
| //--- failed
|
| return(false);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Deletes indicator from chart |
|
| //+------------------------------------------------------------------+
|
| bool CIndicator::DeleteFromChart(const long chart,const int subwin)
|
| {
|
| return(ChartIndicatorDelete(chart,subwin,m_name));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Converting value of ENUM_MA_METHOD into string |
|
| //+------------------------------------------------------------------+
|
| string CIndicator::MethodDescription(const int val)
|
| {
|
| //--- select by value
|
| switch(val)
|
| {
|
| case ENUM_MA_METHOD::MODE_SMA:
|
| return("SMA");
|
| case ENUM_MA_METHOD::MODE_EMA:
|
| return("EMA");
|
| case ENUM_MA_METHOD::MODE_SMMA:
|
| return("SMMA");
|
| case ENUM_MA_METHOD::MODE_LWMA:
|
| return("LWMA");
|
| }
|
| //--- wrong value
|
| return("MethodUnknown="+IntegerToString(val));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Converting value of ENUM_APPLIED_PRICE into string |
|
| //+------------------------------------------------------------------+
|
| string CIndicator::PriceDescription(const int val)
|
| {
|
| //--- select by value
|
| switch(val)
|
| {
|
| case ENUM_APPLIED_PRICE::PRICE_CLOSE:
|
| return("Close");
|
| case ENUM_APPLIED_PRICE::PRICE_OPEN:
|
| return("Open");
|
| case ENUM_APPLIED_PRICE::PRICE_HIGH:
|
| return("High");
|
| case ENUM_APPLIED_PRICE::PRICE_LOW:
|
| return("Low");
|
| case ENUM_APPLIED_PRICE::PRICE_MEDIAN:
|
| return("Median");
|
| case ENUM_APPLIED_PRICE::PRICE_TYPICAL:
|
| return("Typical");
|
| case ENUM_APPLIED_PRICE::PRICE_WEIGHTED:
|
| return("Weighted");
|
| default:
|
| //--- is an indicator handle
|
| if(val>=10)
|
| return("AppliedHandle="+IntegerToString(val));
|
| //---
|
| break;
|
| }
|
| //--- wrong value
|
| return("PriceUnknown="+IntegerToString(val));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Converting value of ENUM_APPLIED_VOLUME into string |
|
| //+------------------------------------------------------------------+
|
| string CIndicator::VolumeDescription(const int val)
|
| {
|
| //--- select by value
|
| switch(val)
|
| {
|
| case ENUM_APPLIED_VOLUME::VOLUME_TICK:
|
| return("Tick");
|
| case ENUM_APPLIED_VOLUME::VOLUME_REAL:
|
| return("Real");
|
| }
|
| //--- wrong value
|
| return("VolumeUnknown="+IntegerToString(val));
|
| }
|
| //+------------------------------------------------------------------+
|