
Timeseries in DoEasy library (part 42): Abstract indicator buffer object class
Contents
- Concept
- CBuffer abstract buffer class
- Checking creation of buffer objects in the indicator
- What's next?
Concept
In the previous article, I have developed a sample indicator using DoEasy library timeseries objects for its work. In order to store and access the buffer data, I have created the buffer structure featuring all the necessary data for correct identification of the buffer belonging to a chart symbol and period for plotting and drawing indicator lines in the form of candlesticks. However, it would be inconvenient and impractical to create the structures, whose fields correspond to the required type of line drawing, for each indicator. It is much more convenient to use the object class of the indicator buffer which allows us to easily create any types of buffers by style and drawing method.
I will start developing such a tool in this article.
The object structure is to consist of the basic indicator buffer class (the so called "abstract buffer object") containing all general properties inherent in all indicator buffers regardless of their drawing type. The buffer object classes containing the clarifying data on specific buffer type are to be inherited from this abstract buffer. These descendant classes will precisely define the drawing type and feature individual properties unique to this type of indicator buffer.
In the current article, we will write the object class of the abstract indicator buffer. The object is to contain all indicator buffer properties from the ENUM_PLOT_PROPERTY_INTEGER, ENUM_PLOT_PROPERTY_DOUBLE and ENUM_PLOT_PROPERTY_STRING enumerations, as well as a few additional properties:
- for specifying a buffer type:
- data buffer,
- calculated buffer;
- buffer status (define the drawing type of indicator lines)
descendant objects are to be sorted by the buffer status — a separate descendant class of the abstract buffer class is created for each status; - buffer working symbol:
- current,
- strictly specified;
- buffer timeframe:
- current,
- strictly specified;
- The serial number of the drawn buffer is a buffer number displayed in the terminal's DataWindow (these numbers do not coincide with the indices of double arrays assigned to the buffer);
- The buffer usage flag — the indicator is to make it possible to enable/disable the display of buffer lines and values in the DataWindow by checking/unchecking the flag;
- The index of the base data buffer — auxiliary data on the index of the first array from all arrays used to draw the buffer lines;
- The amount of buffer data — auxiliary data on the real amount of arrays used to draw buffer graphics. It is necessary to calculate the color buffer index and the index of the next array the index can be assigned to as the base data buffer;
- Color buffer index is an auxiliary data on the array index used as the color buffer;
- The free array index for assigning as the next indicator buffer — auxiliary data on the calculated index of the base data buffer for the next buffer object.
As a result, after creating all the necessary classes for working with the indicator buffers, we obtain the ability to simply "tell" our program "create the buffer of this type", and the library will create the buffer and any subsequent buffers without the need to independently declare arrays, assign their properties and binding to indicator buffers. We simply obtain the buffer list, from which we will access any of the previously created indicator buffers and buffer data.
Since MQL5 provides the ability to create two similar drawing styles — monochrome and colored ones, all buffers created by the library are to be colored. If you want to create a monochrome line, a single color for all data displayed by the buffer is set.
When using different colors for coloring the bars, the specified number of colors will be used to display the line on each bar.
CBuffer abstract buffer class
For the class operation, we will need text messages with descriptions of buffer properties.
Open \MQL5\Include\DoEasy\Datas.mqh and add indices of new messages to it:
MSG_LIB_TEXT_WAIT, // Wait MSG_LIB_TEXT_END, // End MSG_LIB_TEXT_PERIOD_CURRENT, // Current chart period
...
MSG_LIB_TEXT_TS_TEXT_ATTEMPT, // Attempt: MSG_LIB_TEXT_TS_TEXT_WAIT_FOR_SYNC, // Waiting for data synchronization ... //--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, // Base data buffer index MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, // Plotted buffer serial number MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, // Color buffer index MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, // Number of data buffers MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT, // Index of the free array to be assigned as the next indicator buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Buffer (timeframe) data period MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Buffer status MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Buffer type MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Active MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Arrow code MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // The vertical shift of the arrows MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Display construction values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Indicator graphical construction shift by time axis in bars MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Line style MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Line width MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Drawing color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, // Indicator buffer with graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE, // No drawing MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING, // Color filling between two levels MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE, // Line MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM, // Histogram from the zero line MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW, // Drawing with arrows MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION, // Segments MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2, // Histogram on two indicator buffers MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG, // Zigzag MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS, // Display as bars MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES, // Display as candles MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE, // Calculated buffer MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA, // Colored data buffer MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID, // Solid line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH, // Dashed line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT, // Dotted line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT, // Dot-dash line MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT, // Dash - two dots }; //+------------------------------------------------------------------+
and message texts corresponding to newly added indices:
{"Ожидание","Wait"}, {"Окончание","End"}, {"Текущий период графика","Current chart period"},
...
{"Попытка: ","Attempt: "}, {"Ожидание синхронизации данных ...","Waiting for data synchronization ..."}, {"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in the DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in the DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along the time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","The thickness of the drawing line"}, {"Количество цветов","The number of colors"}, {"Цвет отрисовки","The index of a buffer containing the drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","An empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","The name of the indicator graphical series to display in the DataWindow"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Нет отрисовки","No drawing"}, {"Цветовая заливка между двумя уровнями","Color fill between the two levels"}, {"Линия","Line"}, {"Гистограмма от нулевой линии","Histogram from the zero line"}, {"Отрисовка стрелками","Drawing arrows"}, {"Отрезки","Section"}, {"Гистограмма на двух индикаторных буферах","Histogram of the two indicator buffers"}, {"Зигзаг","Zigzag"}, {"Отображение в виде баров","Display as a sequence of bars"}, {"Отображение в виде свечей","Display as a sequence of candlesticks"}, {"Расчётный буфер","Calculated buffer"}, {"Цветной буфер данных","Colored Data buffer"}, {"Сплошная линия","Solid line"}, {"Прерывистая линия","Broken line"}, {"Пунктирная линия","Dotted line"}, {"Штрих-пунктирная линия","Dash-dot line"}, {"Штрих - две точки","Dash - two points"}, }; //+---------------------------------------------------------------------+
When creating library objects to be stored in the object collection, ID of the appropriate collection is assigned to each object. The same is done for buffer objects: create indicator buffer collection ID and assign it to the abstract buffer object. Accordingly, each object inheriting from an abstract buffer will have an ID indicating that it belongs to the buffer collection.
To create an abstract buffer object, we need to define and describe all its properties. Using these properties, we are always able to find the required buffer object in the buffer collection for further work.
Open \MQL5\Include\DoEasy\Defines.mqh and add the indicator buffer collection ID:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID //--- Data parameters for file operations
At the very end of the file, add enumerations of the buffer object status and its type, define integer, real and string object properties, as well as write possible criteria of buffer object sorting in their collection list:
//+------------------------------------------------------------------+ //| Data for working with indicator buffers | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Abstract buffer status (by drawing style) | //+------------------------------------------------------------------+ enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // No drawing BUFFER_STATUS_FILLING, // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // Line BUFFER_STATUS_HISTOGRAM, // Histogram from the zero line BUFFER_STATUS_ARROW, // Drawing with arrows BUFFER_STATUS_SECTION, // Segments BUFFER_STATUS_HISTOGRAM2, // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // Zigzag style BUFFER_STATUS_BARS, // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // Display as candles (MQL5) }; //+------------------------------------------------------------------+ //| Buffer type | //+------------------------------------------------------------------+ enum ENUM_BUFFER_TYPE { BUFFER_TYPE_CALCULATE, // Calculated buffer BUFFER_TYPE_DATA, // Colored data buffer }; //+------------------------------------------------------------------+ //| Buffer integer properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Plotted buffer serial number BUFFER_PROP_STATUS, // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration BUFFER_PROP_TYPE, // Buffer type (from the ENUM_BUFFER_TYPE enumeration) BUFFER_PROP_TIMEFRAME, // Buffer period data (timeframe) BUFFER_PROP_ACTIVE, // Buffer usage flag BUFFER_PROP_ARROW_CODE, // Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, // The vertical shift of the arrows for DRAW_ARROW style BUFFER_PROP_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow BUFFER_PROP_SHOW_DATA, // Flag of displaying construction values in DataWindow BUFFER_PROP_DRAW_TYPE, // Graphical construction type (from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_SHIFT, // Indicator graphical construction shift by time axis in bars BUFFER_PROP_LINE_STYLE, // Line style BUFFER_PROP_LINE_WIDTH, // Line width BUFFER_PROP_COLOR_INDEXES, // Number of colors BUFFER_PROP_COLOR, // Drawing color BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_BASE, // Base data buffer index BUFFER_PROP_INDEX_COLOR, // Color buffer index BUFFER_PROP_INDEX_NEXT, // Index of the free array to be assigned as the next indicator buffer }; #define BUFFER_PROP_INTEGER_TOTAL (19) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (6) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+ //| Buffer real properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_DOUBLE { BUFFER_PROP_EMPTY_VALUE = BUFFER_PROP_INTEGER_TOTAL, // Empty value for plotting where nothing will be drawn }; #define BUFFER_PROP_DOUBLE_TOTAL (1) // Total number of real buffer properties #define BUFFER_PROP_DOUBLE_SKIP (0) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+ //| Buffer string properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Buffer symbol BUFFER_PROP_LABEL, // Name of the graphical indicator series displayed in DataWindow }; #define BUFFER_PROP_STRING_TOTAL (2) // Total number of string buffer properties //+------------------------------------------------------------------+ //| Possible buffer sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = 0, // Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, // Sort by the buffer data period (timeframe) SORT_BY_BUFFER_ACTIVE, // Sort by the buffer usage flag SORT_BY_BUFFER_ARROW_CODE, // Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, // Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_DRAW_BEGIN, // Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, // Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_DRAW_TYPE, // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_SHIFT, // Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_LINE_STYLE, // Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, // Sort by the line width SORT_BY_BUFFER_COLOR_INDEXES, // Sort by a number of attempts SORT_BY_BUFFER_COLOR, // Sort by the drawing color //--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Sort by the buffer symbol SORT_BY_BUFFER_LABEL, // Sort by the name of the graphical indicator series displayed in DataWindow }; //+------------------------------------------------------------------+
All these properties, as well as their definition, assignment and usage, are standard for the library. We have considered them many times, so there is no point in delving into each individual enumeration and macro substitution here. I hope, you are already familiar with their purpose and no explanations are required. However, if you have any questions, feel free to ask them in the comments below.
We have prepared all the necessary data. Now it is time to start creating the abstract buffer object class.
In \MQL5\Include\DoEasy\Objects\, create Indicators\ folder containing the Buffer.mqh file of the CBuffer class.
The class of the base object of all CBaseObj library objects serves as the base class for the abstract buffer object class.
Include the class file to the buffer class file:
//+------------------------------------------------------------------+ //| Buffer.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract indicator buffer class | //+------------------------------------------------------------------+ class CBuffer : public CBaseObj {
The set of basic methods of library objects is standard for each object. It allows working with the properties inherent in each object in the same way for each individual object. I have considered the object construction principles at the very beginning of the library description.
Here everything is standard as well: three object property arrays (integer, real and string ones), methods of returning real indices of real and string properties, as well as methods of setting, receiving and retrieving the description of the specified property from these arrays. Three virtual methods returning the flags of using specified properties and two comparison methods — for searching and sorting in the collection by the specified property as well as for comparing two objects for equality. Two constructors — the default and closed parametric one and two methods for displaying all buffer object properties and its short description in the journal:
//+------------------------------------------------------------------+ //| Abstract indicator buffer class | //+------------------------------------------------------------------+ class CBuffer : public CBaseObj { private: long m_long_prop[BUFFER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[BUFFER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[BUFFER_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the buffer's (1) double and (2) string properties are located at int IndexProp(ENUM_BUFFER_PROP_DOUBLE property) const { return(int)property-BUFFER_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_BUFFER_PROP_STRING property) const { return(int)property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL; } //--- Set the graphical construction type void SetDrawType(void); public: //--- Array of the (1) drawn indicator buffer and (2) color buffer double DataArray[]; double ColorArray[]; //--- Set buffer's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_BUFFER_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string buffer properties from the properties array long GetProperty(ENUM_BUFFER_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_BUFFER_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_BUFFER_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Get description of buffer's (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property); string GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property); string GetPropertyDescription(ENUM_BUFFER_PROP_STRING property); //--- Return the flag of the buffer supporting the property virtual bool SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_STRING property) { return true; } //--- Compare CBuffer objects by all possible properties (for sorting the lists by a specified buffer object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CBuffer objects by all properties (to search for equal buffer objects) bool IsEqual(CBuffer* compared_obj) const; //--- Default constructor CBuffer(void){;} //protected: //--- Protected parametric constructor CBuffer(ENUM_BUFFER_STATUS status_buffer,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array); public: //--- Send description of buffer properties to the journal (full_prop=true - all properties, false - only supported ones) void Print(const bool full_prop=false); //--- Display a short buffer description in the journal (implementation in the descendants) virtual void PrintShort(void) {;}
Standard data and methods are highlighted here.
The protected parametric constructor should be present in the protected class section but, in the current article, the constructor will be made public to check the created object operation.
This is why the protected: access specifier has been commented out.
One private method of setting the buffer graphical construction type, two public arrays for their binding as data array and color array of the indicator buffer have remained undescribed:
//--- Set the graphical construction type void SetDrawType(void); public: //--- Array of the (1) drawn indicator buffer and (2) color buffer double DataArray[]; double ColorArray[];
Depending on the buffer status (inherited objects are "bound" to its status), we will define the type of the buffer graphical construction.
If we look closely at the buffer status enumeration constants
//+------------------------------------------------------------------+ //| Abstract buffer status (by drawing style) | //+------------------------------------------------------------------+ enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // No drawing BUFFER_STATUS_FILLING, // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // Line BUFFER_STATUS_HISTOGRAM, // Histogram from the zero line BUFFER_STATUS_ARROW, // Drawing with arrows BUFFER_STATUS_SECTION, // Segments BUFFER_STATUS_HISTOGRAM2, // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // Zigzag style BUFFER_STATUS_BARS, // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // Display as candles (MQL5) }; //+------------------------------------------------------------------+
and compare the order of the constants with the order of constants of almost the same name in the ENUM_DRAW_TYPE enumeration using a simple loop by the number of constants in the enumeration,
for(int i=0;i<18;i++) Print(EnumToString((ENUM_DRAW_TYPE)i)," = ",i); 2020.04.15 12:51:53.725 DRAW_NONE = 0 2020.04.15 12:51:53.725 DRAW_LINE = 1 2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2 2020.04.15 12:51:53.725 DRAW_ARROW = 3 2020.04.15 12:51:53.725 DRAW_SECTION = 4 2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5 2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6 2020.04.15 12:51:53.725 DRAW_FILLING = 7 2020.04.15 12:51:53.725 DRAW_BARS = 8 2020.04.15 12:51:53.725 DRAW_CANDLES = 9 2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11 2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12 2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14 2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15 2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16 2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17
we will see that the orders match by the graphical construction type.
The difference is that the enumeration of drawing styles has constants for monochrome and color buffers, and there is one style that does not need a color buffer (the color filling between the two levels).
All buffers are to be colored. To set the drawing style, check the buffer status and drawing type passed to the SetDrawType() method and, depending on them, either set no drawing, or set filling the space between the two levels with color, or shift the status enumeration index by 8 units so that the constant value corresponds to that of the color buffer from the drawing style enumeration.
Beyond the class body, implement the method:
//+------------------------------------------------------------------+ //| Set the graphical construction type | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(void) { ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8)); this.SetProperty(BUFFER_PROP_DRAW_TYPE,type); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type); } //+------------------------------------------------------------------+
If the buffer type is BUFFER_TYPE_CALCULATE (0) or BUFFER_STATUS_NONE (0), the drawing style is set to "No drawing". If the buffer status is BUFFER_STATUS_FILLING (filling with color), the appropriate drawing style is set. All the remaining values are simply increased by 8. This shift indicates the constant of the colored drawing style.
Below is an example of increasing the ENUM_BUFFER_STATUS enumeration constant value by 8 and the value in the ENUM_DRAW_TYPE enumeration it falls on in this case:
enum ENUM_BUFFER_STATUS { BUFFER_STATUS_NONE, // 0 // No drawing BUFFER_STATUS_FILLING, // 1 // Color filling between two levels (MQL5) BUFFER_STATUS_LINE, // 2 +8 = 10 // Line BUFFER_STATUS_HISTOGRAM, // 3 +8 = 11 // Histogram from the zero line BUFFER_STATUS_ARROW, // 4 +8 = 12 // Drawing with arrows BUFFER_STATUS_SECTION, // 5 +8 = 13 // Segments BUFFER_STATUS_HISTOGRAM2, // 6 +8 = 14 // Histogram on two indicator buffers BUFFER_STATUS_ZIGZAG, // 7 +8 = 15 // Zigzag style BUFFER_STATUS_BARS, // 8 +8 = 16 // Display as bars (MQL5) BUFFER_STATUS_CANDLES, // 9 +8 = 17 // Display as candles (MQL5) }; 2020.04.15 12:51:53.725 DRAW_NONE = 0 2020.04.15 12:51:53.725 DRAW_LINE = 1 2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2 2020.04.15 12:51:53.725 DRAW_ARROW = 3 2020.04.15 12:51:53.725 DRAW_SECTION = 4 2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5 2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6 2020.04.15 12:51:53.725 DRAW_FILLING = 7 2020.04.15 12:51:53.725 DRAW_BARS = 8 2020.04.15 12:51:53.725 DRAW_CANDLES = 9 2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11 2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12 2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13 2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14 2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15 2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16 2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17
Thus, its drawing style is set based on the buffer status value.
Add all the remaining methods for setting and returning the buffer properties and displaying the description of its properties to the journal to the public section of the class:
public: //--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag //--- (6) number of initial bars without drawing, (7) flag of displaying construction values in DataWindow, //--- (8) shift of the indicator graphical construction along the time axis, (9) line style, (10) line width, //--- (11) number of colors, (12) drawing color, (13) empty value and (14) graphical series name displayed in DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetEmptyValue(const double value); void SetLabel(const string label); //--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index, //--- (4) index of the first free bound array, (5) buffer data period (timeframe) (6) buffer status, //--- (7) buffer type, (8) buffer usage flag, (9) arrow code, (10) arrow shift for DRAW_ARROW style, //--- (11) Number of initial bars that are not drawn and values in DataWindow, (12) graphical construction type, //--- (13) flag of displaying construction values in DataWindow, (14) indicator graphical construction shift along the time axis, //--- (15) drawing line style, (16) drawing line width, (17) number of colors, (18) drawing color, //--- (19) set empty value, (20) buffer symbol and (21) name of the indicator graphical series displayed in DataWindow int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int NumberColors(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } //--- Return descriptions of the (1) buffer status, (2) buffer type, (3) buffer usage flag, (4) flag of displaying construction values in DataWindow, //--- (5) drawing line style, (6) set empty value, (7) graphical construction type and (8) used timeframe string GetStatusDescription(bool draw_type=false)const; string GetTypeBufferDescription(void) const; string GetActiveDescription(void) const; string GetShowDataDescription(void) const; string GetLineStyleDescription(void) const; string GetEmptyValueDescription(void) const; string GetDrawTypeDescription(void) const; string GetTimeframeDescription(void) const; //--- Return the size of the data buffer array int GetDataTotal(void) const { return ::ArraySize(this.DataArray); } }; //+------------------------------------------------------------------+
Let's consider the implementation of declared methods and closed parametric constructor.
Closed parametric constructor:
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array) { this.m_type=COLLECTION_BUFFERS_ID; //--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = false; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0xFB; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = 1; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = 1; this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = 1; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS); this.m_long_prop[BUFFER_PROP_INDEX_NEXT] = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+1; this.SetDrawType(); //--- Save real properties this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = EMPTY_VALUE; //--- Save string properties this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? "Buffer "+(string)this.IndexPlot() : NULL); //--- Bind indicator buffers with arrays ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_BASE),this.DataArray,INDICATOR_DATA); ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorArray,INDICATOR_COLOR_INDEX); //--- Set integer buffer parameters ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR_INDEXES)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR)); //--- Set real buffer parameters ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string buffer parameters ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL)); } //+------------------------------------------------------------------+
The constructor inputs pass the status and type of the created buffer object, drawn buffer index (Market Watch index) and base array index (the base array is the very first array from the general list of arrays used to construct the buffer).
Assign the type of belonging to the buffer object collection to the object and fill in integer, real and string object properties with default values.
Next, bind the indicator buffers with the arrays and set integer, real and string properties from the newly filled default values of same-type buffer object properties to the drawn buffer.
These actions are enough for creating a one-color indicator buffer with the specified drawing type. No arrays should be declared, set and assigned when creating such a buffer from the indicator — all the necessary buffer objects are already present and assigned (if necessary) in the buffer object. The BUFFER_PROP_INDEX_PLOT property contains the drawn buffer index used to set and receive the buffer data from its indicator program.
I will mention the standard methods here without delving into them. I have already described similar methods of other objects in all previous articles. Besides, I have considered the methods in detail when creating the very first library object:
Comparison methods for searching and sorting, as well as for returning the flag of the equality of two compared objects:
//+------------------------------------------------------------------+ //| Class methods | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Compare CBuffer objects by all possible properties | //+------------------------------------------------------------------+ int CBuffer::Compare(const CObject *node,const int mode=0) const { const CBuffer *compared_obj=node; //--- compare integer properties of two buffers if(mode<BUFFER_PROP_INTEGER_TOTAL) { long value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two buffers else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL) { double value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two buffers else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL+BUFFER_PROP_STRING_TOTAL) { string value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_BUFFER_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Compare CBuffer objects by all properties | //+------------------------------------------------------------------+ bool CBuffer::IsEqual(CBuffer *compared_obj) const { int beg=0, end=BUFFER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=BUFFER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
The method for displaying all buffer object properties:
//+------------------------------------------------------------------+ //| Display buffer properties in the journal | //+------------------------------------------------------------------+ void CBuffer::Print(const bool full_prop=false) { ::Print("============= ", CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": ", this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"", " ==================" ); int beg=0, end=BUFFER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BUFFER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ", CMessage::Text(MSG_LIB_PARAMS_LIST_END),": ", this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"", " ==================\n" ); } //+------------------------------------------------------------------+
The methods returning the description of integer, real and string buffer object properties:
//+------------------------------------------------------------------+ //| Return description of a buffer's integer property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString(this.Color(),true) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString(this.Color(),true) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of a buffer's real property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property) { return ( property==BUFFER_PROP_EMPTY_VALUE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetEmptyValueDescription() ) : "" ); } //+------------------------------------------------------------------+ //| Return description of a buffer's string property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : this.Label()) ) : "" ); } //+------------------------------------------------------------------+
Let's have a look at the implementation of the remaining added methods to the class public section.
The method returning the buffer status description:
//+------------------------------------------------------------------+ //| Return the buffer status description | //+------------------------------------------------------------------+ string CBuffer::GetStatusDescription(bool draw_type=false) const { string type= ( this.Status()==BUFFER_STATUS_NONE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE) : this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW) : this.Status()==BUFFER_STATUS_BARS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS) : this.Status()==BUFFER_STATUS_CANDLES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES) : this.Status()==BUFFER_STATUS_FILLING ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING) : this.Status()==BUFFER_STATUS_HISTOGRAM ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM) : this.Status()==BUFFER_STATUS_HISTOGRAM2 ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2) : this.Status()==BUFFER_STATUS_LINE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE) : this.Status()==BUFFER_STATUS_SECTION ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION) : this.Status()==BUFFER_STATUS_ZIGZAG ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG) : "Unknown" ); return(!draw_type ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME)+" \""+type+"\"" : type); } //+------------------------------------------------------------------+
Since the buffer status also defines the drawing style, the method receives the flag specifying how to return the description:
— if the buffer status is requested (draw_type set to false), the buffer status is returned in the following form
Buffer status: Indicator buffer with graphical construction type "Line"
— if the drawing type is requested (draw_type is set to true), the drawing type is returned in the following form
Graphical construction type: Line
All methods returning object property descriptions are quite simple, leave them for independent study:
//+------------------------------------------------------------------+ //| Return the buffer type description | //+------------------------------------------------------------------+ string CBuffer::GetTypeBufferDescription(void) const { return ( this.TypeBuffer()==BUFFER_TYPE_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA) : this.TypeBuffer()==BUFFER_TYPE_CALCULATE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE) : "Unknown" ); } //+------------------------------------------------------------------+ //| Return description of the buffer usage flag | //+------------------------------------------------------------------+ string CBuffer::GetActiveDescription(void) const { return(this.IsActive() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)); } //+---------------------------------------------------------------------+ //|Return description of displaying construction values in DataWindow | //+---------------------------------------------------------------------+ string CBuffer::GetShowDataDescription(void) const { return(this.IsShowData() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)); } //+------------------------------------------------------------------+ //| Return description of the drawing line style | //+------------------------------------------------------------------+ string CBuffer::GetLineStyleDescription(void) const { return ( this.LineStyle()==STYLE_SOLID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID) : this.LineStyle()==STYLE_DASH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH) : this.LineStyle()==STYLE_DOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT) : this.LineStyle()==STYLE_DASHDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT) : this.LineStyle()==STYLE_DASHDOTDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT) : "Unknown" ); } //+------------------------------------------------------------------+ //| Return description of the set empty value | //+------------------------------------------------------------------+ string CBuffer::GetEmptyValueDescription(void) const { return(this.EmptyValue()<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : "EMPTY_VALUE"); } //+------------------------------------------------------------------+ //| Return description of the graphical construction type | //+------------------------------------------------------------------+ string CBuffer::GetDrawTypeDescription(void) const { return this.GetStatusDescription(true); } //+------------------------------------------------------------------+ //| Return description of the used timeframe | //+------------------------------------------------------------------+ string CBuffer::GetTimeframeDescription(void) const { string timeframe=TimeframeDescription(this.Timeframe()); return(this.Timeframe()==PERIOD_CURRENT ? CMessage::Text(MSG_LIB_TEXT_PERIOD_CURRENT)+" ("+timeframe+")" : timeframe); } //+------------------------------------------------------------------+
The methods for setting different buffer object properties:
//+------------------------------------------------------------------+ //| Set the number of initial bars | //| without drawing and values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetDrawBegin(const int value) { this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value); } //+------------------------------------------------------------------+ //| Set the flag of displaying | //| construction values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetShowData(const bool flag) { this.SetProperty(BUFFER_PROP_SHOW_DATA,flag); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag); } //+------------------------------------------------------------------+ //| Set the indicator graphical construction shift | //+------------------------------------------------------------------+ void CBuffer::SetShift(const int shift) { this.SetProperty(BUFFER_PROP_SHIFT,shift); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift); } //+------------------------------------------------------------------+ //| Set the line style | //+------------------------------------------------------------------+ void CBuffer::SetStyle(const ENUM_LINE_STYLE style) { this.SetProperty(BUFFER_PROP_LINE_STYLE,style); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style); } //+------------------------------------------------------------------+ //| Set the line width | //+------------------------------------------------------------------+ void CBuffer::SetWidth(const int width) { this.SetProperty(BUFFER_PROP_LINE_WIDTH,width); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width); } //+------------------------------------------------------------------+ //| Set the number of colors | //+------------------------------------------------------------------+ void CBuffer::SetColorNumbers(const int number) { this.SetProperty(BUFFER_PROP_COLOR_INDEXES,number); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,number); } //+------------------------------------------------------------------+ //| Set the drawing color | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour) { this.SetProperty(BUFFER_PROP_COLOR,colour); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,colour); } //+------------------------------------------------------------------+ //| Set the "empty" value for construction | //| without drawing | //+------------------------------------------------------------------+ void CBuffer::SetEmptyValue(const double value) { this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value); ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value); } //+------------------------------------------------------------------+ //| Set the drawing color | //+------------------------------------------------------------------+ void CBuffer::SetLabel(const string label) { this.SetProperty(BUFFER_PROP_LABEL,label); ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label); } //+------------------------------------------------------------------+
The value passed to the method is first written to the appropriate buffer object property, then the property is set for the drawn buffer by its index.
Currently, this is all that needs to be done for creating the abstract indicator buffer object.
Checking creation of buffer objects in the indicator
To check the abstract buffer object operation, use the indicator from the previous article and save it in \MQL5\Indicators\TestDoEasy\Part42\ as TestDoEasyPart42.mq5.
Remove all unnecessary things from the indicator.
We do not need any buttons, button pressing handling functions and buffer data filling functions, so remove them from the code leaving only the elements directly related to the library operation.
Besides, let's move the functions for copying data from OnCalculate() to the library price structure from the indicator code to the file of the library service functions. Open \MQL5\Include\DoEasy\Services\DELib.mqh and add two functions to it:
//+------------------------------------------------------------------+ //| Copy data from the first OnCalculate() form to the structure | //+------------------------------------------------------------------+ void CopyData(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- Get the array indexing flag as in the timeseries. If failed, //--- set the indexing direction for the array as in the timeseries bool as_series_price=ArrayGetAsSeries(price); if(!as_series_price) ArraySetAsSeries(price,true); //--- Copy the array zero bar to the OnCalculate() SDataCalculate data structure rates_data.rates_total=rates_total; rates_data.prev_calculated=prev_calculated; rates_data.begin=begin; rates_data.price=price[0]; //--- Return the array's initial indexing direction if(!as_series_price) ArraySetAsSeries(price,false); } //+------------------------------------------------------------------+ //| Copy data from the second OnCalculate() form to the structure | //+------------------------------------------------------------------+ void CopyData(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Get the array indexing flags as in the timeseries. If failed, //--- set the indexing direction or the arrays as in the timeseries bool as_series_time=ArrayGetAsSeries(time); if(!as_series_time) ArraySetAsSeries(time,true); bool as_series_open=ArrayGetAsSeries(open); if(!as_series_open) ArraySetAsSeries(open,true); bool as_series_high=ArrayGetAsSeries(high); if(!as_series_high) ArraySetAsSeries(high,true); bool as_series_low=ArrayGetAsSeries(low); if(!as_series_low) ArraySetAsSeries(low,true); bool as_series_close=ArrayGetAsSeries(close); if(!as_series_close) ArraySetAsSeries(close,true); bool as_series_tick_volume=ArrayGetAsSeries(tick_volume); if(!as_series_tick_volume) ArraySetAsSeries(tick_volume,true); bool as_series_volume=ArrayGetAsSeries(volume); if(!as_series_volume) ArraySetAsSeries(volume,true); bool as_series_spread=ArrayGetAsSeries(spread); if(!as_series_spread) ArraySetAsSeries(spread,true); //--- Copy the arrays' zero bar to the OnCalculate() SDataCalculate data structure rates_data.rates_total=rates_total; rates_data.prev_calculated=prev_calculated; rates_data.rates.time=time[0]; rates_data.rates.open=open[0]; rates_data.rates.high=high[0]; rates_data.rates.low=low[0]; rates_data.rates.close=close[0]; rates_data.rates.tick_volume=tick_volume[0]; rates_data.rates.real_volume=(#ifdef __MQL5__ volume[0] #else 0 #endif); rates_data.rates.spread=(#ifdef __MQL5__ spread[0] #else 0 #endif); //--- Return the arrays' initial indexing direction if(!as_series_time) ArraySetAsSeries(time,false); if(!as_series_open) ArraySetAsSeries(open,false); if(!as_series_high) ArraySetAsSeries(high,false); if(!as_series_low) ArraySetAsSeries(low,false); if(!as_series_close) ArraySetAsSeries(close,false); if(!as_series_tick_volume) ArraySetAsSeries(tick_volume,false); if(!as_series_volume) ArraySetAsSeries(volume,false); if(!as_series_spread) ArraySetAsSeries(spread,false); } //+------------------------------------------------------------------+
As a result, we cleaned up the indicator code for the current verification and future use. The two moved functions are necessary to work with indicators within the library, so the file of the library service functions is the best place for them.
The indicator "header" is as follows:
//+------------------------------------------------------------------+ //| TestDoEasyPart42.mq5 | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Indicators\Buffer.mqh> //--- properties #property indicator_chart_window #property indicator_buffers 4 #property indicator_plots 2 //--- classes //--- enums //--- defines //--- structures //--- input variables /*sinput*/ ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_DEFINES; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers CArrayObj list_buffers; // Temporary list for storing two buffer objects //--- global variables CEngine engine; // CEngine library main object string prefix; // Prefix of graphical object names int min_bars; // The minimum number of bars for the indicator calculation int used_symbols_mode; // Mode of working with symbols string array_used_symbols[]; // The array for passing used symbols to the library string array_used_periods[]; // The array for passing used timeframes to the library //+------------------------------------------------------------------+
Here we order the compiler to create the indicator in a separate window and set four buffers for it (two drawn and two color ones).
The remaining buffer parameters are set during and after creating indicator buffer objects.
Add the dynamic array of CArrayObj pointers as the indicator buffer. It is to store created buffers.
When working with buffer objects, we do not need to declare double arrays for assigning them as indicator buffers. Everything is located inside the created buffer objects and assigned when creating them in the indicator's OnInit() handler:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialize DoEasy library OnInitDoEasy(); //--- Set indicator global variables prefix=engine.Name()+"_"; //--- Get the index of the maximum used timeframe in the array, //--- calculate the number of bars of the current period fitting in the maximum used period //--- Use the obtained value if it exceeds 2, otherwise use 2 int index=ArrayMaximum(ArrayUsedTimeframes); int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]); min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2); //--- Check and remove remaining indicator graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel //--- Check playing a standard sound using macro substitutions engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Create two buffer objects CBuffer *buffer0=new CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA, 0, 0); CBuffer *buffer1=new CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA, 1, buffer0.IndexNextBuffer()); //--- Set non-default values for the second buffer's "empty" value and color buffer1.SetEmptyValue(0); buffer1.SetColor(clrBlue); //--- Add both buffers to the list of indicator buffers list_buffers.Add(buffer0); list_buffers.Add(buffer1); //--- Print data of the created buffers buffer0.Print(); buffer1.Print(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Here I have created two objects of abstract indicator buffers.
The first one has the "Drawing with arrows" status. Set 0 as the drawn buffer index and the buffer base array index — this is the very first buffer, and all indices should have initial values here.
We are going to get rid of specifying array indices while creating buffer objects when creating the indicator buffer collection — all indices are to be set automatically there, no user intervention is to be required.
The second object has the "Line" status. Set 1 (following zero) as the drawn buffer index. Also, set the value returned from the first buffer object as the base array index. This is the value indicating the index of the next free array for specifying it as the base array for the next buffer.
Besides, 0 (zero) is assigned to the second created buffer as the "empty" value, while the line color is set to "Blue" (in order to check how the values are set to buffer object properties).
Add both buffers to the previously declared list_buffers list and send to the journal all properties of both newly created buffers.
Here we do not check if the buffer objects have been successfully created and added to the list since this is a test indicator, and we are free to neglect the control over object creation and adding to the list for the sake of fast verification of buffer objects.
Let's "clear" the OnCalculate() handler from the previous article leaving only the most necessary elements for our test: we only need to check the results of creating two buffer objects and their correct assignment as indicator buffers.
How can we do that?
The buffer creation is checked in OnInit() when we print all the properties of each created buffer to the journal. Successful assignment of the created objects as indicator buffers can be checked only in OnCalculate(). To do this, simply compare the size of arrays used in objects as indicator buffers with the number of bars on the symbol.
As soon as we assign an array as an indicator buffer, the executed terminal subsystem takes these arrays under its wing, allocates memory for them and manages the array size. Therefore, we only need to get each of the objects from the list_buffers list and compare the size of the array assigned as a buffer with the rates_total value in OnCalculate(). Their equality indicates that the terminal subsystem has taken the arrays from the buffer objects under its control.
To avoid displaying entries at each tick, assigning buffer object arrays as indicator buffers is verified during the very first indicator calculation when the calculated limit value exceeds one:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //+------------------------------------------------------------------+ //| OnCalculate code block for working with the library: | //+------------------------------------------------------------------+ //--- Pass the current symbol data from OnCalculate() to the price structure CopyData(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread); //--- Check for the minimum number of bars for calculation if(rates_total<min_bars || Point()==0) return 0; //--- Handle the Calculate event in the library //--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick if(engine.0) return 0; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the library timer EventsHandling(); // Working with library events } //+------------------------------------------------------------------+ //| OnCalculate code block for working with the indicator: | //+------------------------------------------------------------------+ //--- Set OnCalculate arrays as timeseries ArraySetAsSeries(open,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(time,true); ArraySetAsSeries(tick_volume,true); ArraySetAsSeries(volume,true); ArraySetAsSeries(spread,true); //--- Check and calculate the number of calculated bars //--- If limit = 0, there are no new bars - calculate the current one //--- If limit = 1, a new bar has appeared - calculate the first and the current ones //--- limit > 1 means the first launch or changes in history - the full recalculation of all data int limit=rates_total-prev_calculated; //--- Recalculate the entire history if(limit>1) { //--- In a loop by the number of buffers in the list for(int i=0;i<list_buffers.Total();i++) { //--- get the next buffer and display the type of its graphical construction to the journal //--- together with the double array assigned to the buffer (if all is correct, the size is equal to rates_total) CBuffer *buff=list_buffers.At(i); Print(buff.Label()," type = ",EnumToString(buff.DrawType()),", data total = ",buff.GetDataTotal(),", rates_total=",rates_total); } limit=rates_total-1; } //--- Prepare data //--- Calculate the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { CalculateSeries(i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Find the entire code of the test indicator in the files attached below.
Compile the indicator and launch it on the chart after setting the following parameters:
The journal displays the following entries:
Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, MetaTrader 5 demo --- Initializing "DoEasy" library --- Working with the current symbol only. The number of used symbols: 1 "EURUSD" Working with the specified timeframe list: "M5" "M15" "M30" "H1" EURUSD symbol timeseries: - Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3684 - Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3042 - Timeseries "EURUSD" M30: Requested: 1000, Actual: 0, Created: 0, On the server: 0 - Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6240 Library initialization time: 00:00:00.156 ============= Parameter list start: Colored data buffer[0] "Drawing with arrows" ================== Plotted buffer serial number: 0 Buffer status: Indicator buffer with graphical construction type "Drawing with arrows" Buffer type: Colored data buffer Buffer data period (timeframe): Current chart period (M30) Active: No Arrow code: 251 The vertical shift of the arrows: 0 The number of initial bars that are not drawn and values in DataWindow: 0 Display construction values in DataWindow: Yes Graphical construction type: Drawing with arrows Indicator graphical construction shift by time axis in bars: 0 Line style: Solid line Line width: 1 Number of colors: 1 Drawing color: clrRed Number of data buffers: 1 Base data buffer index: 0 Color buffer index: 1 Index of the array to be assigned as the next indicator buffer: 2 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Buffer symbol: EURUSD Name of the graphical indicator series displayed in DataWindow: Buffer 0 ================== Parameter list end: Colored data buffer[0] "Drawing with arrows" ================== ============= Parameter list start: Colored data buffer[1] "Line" ================== Plotted buffer serial number: 1 Buffer status: Indicator buffer with graphical construction type "Line" Buffer type: Colored data buffer Buffer data period (timeframe): Current chart period (M30) Active: No Arrow code: 251 The vertical shift of the arrows: 0 The number of initial bars that are not drawn and values in DataWindow: 0 Display construction values in DataWindow: Yes Graphical construction type: Line Indicator graphical construction shift by time axis in bars: 0 Line style: Solid line Line width: 1 Number of colors: 1 Drawing color: clrBlue Number of data buffers: 1 Base data buffer index: 2 Color buffer index: 3 Index of the array to be assigned as the next indicator buffer: 4 ------ Empty value for plotting where nothing will be drawn: 0.0 ------ Buffer symbol: EURUSD Name of the graphical indicator series displayed in DataWindow: Buffer 1 ================== Parameter list end: Colored data buffer[1] "Line" ================== "EURUSD" M30 timeseries created successfully: - Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5111 Buffer 0 type = DRAW_COLOR_ARROW, data total = 5111, rates_total=5111 Buffer 1 type = DRAW_COLOR_LINE, data total = 5111, rates_total=5111
After the library messages about timseries creation, the block displaying all properties of each of the two created buffer objects is printed from OnInit(). Next, two messages about the drawing type of each of the created buffers are displayed from OnCalculate(), the size of arrays from buffer objects assigned as indicator buffers are printed and the rates_total value at the moment of the indicator launch is specified.
As we can see, the size of arrays and rates_total match. This means that the arrays of the created buffer objects are controlled by the terminal, and they are used as indicator buffers.
To make sure once again, simply open the indicator properties (Ctrl+I) and go to the Colors tab:
Names and colors are set for both indicator buffers. The name and color have not been specified, except for the default ones set in the buffer object class constructor. In case of the second buffer, we have reset the color to blue after creating it in OnInit().
Everything works as expected. However, this is just the beginning. In order to create various types of indicator buffers, we need to create inherited classes for each of the graphical construction types and work with these classes from the indicator buffer collection.
What's next?
In the next article, we will create descendant objects of the abstract buffer class. These objects are to be used by the library for creating and using indicator buffers in indicator programs based on the DoEasy library.
All files of the current version of the library are attached below together with the test EA files for you to test and download.
Leave your questions, comments and suggestions in the comments.
Please keep in mind that here I have developed the MQL5 test indicator for MetaTrader 5.
The attached files are intended only for MetaTrader 5. The current library version has not been tested in MetaTrader 4.
After creating all classes of indicator buffers and their collections, I will try to implement some MQL5 features in MetaTrader 4.
Previous articles within the series:
Timeseries in DoEasy library (part 35): Bar object and symbol timeseries listTimeseries in DoEasy library (part 36): Object of timeseries for all used symbol periods
Timeseries in DoEasy library (part 37): Timeseries collection - database of timeseries by symbols and periods
Timeseries in DoEasy library (part 38): Timeseries collection - real-time updates and accessing data from the program
Timeseries in DoEasy library (part 39): Library-based indicators - preparing data and timeseries events
Timeseries in DoEasy library (part 40): Library-based indicators - updating data in real time
Timeseries in DoEasy library (part 41): Sample multi-symbol multi-period indicator
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/7821





- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use