//+------------------------------------------------------------------+ //| 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;im_buffer_size && !CSeries::BufferResize(size)) return(false); //-- history is avalible int total=Total(); for(int i=0;i=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)); } //+------------------------------------------------------------------+