Русский 中文 Español Deutsch 日本語 Português
Graphics in DoEasy library (Part 90): Standard graphical object events. Basic functionality

Graphics in DoEasy library (Part 90): Standard graphical object events. Basic functionality

MetaTrader 5Examples | 7 February 2022, 15:36
5 247 0
Artyom Trishkin
Artyom Trishkin

Contents


Concept

When working with graphical objects provided by the client terminal, we may face the need to programmatically define some property values of these objects. The program may track the value of a graphical object line and execute the embedded algorithm, for example when the line is crossed by the price. The line may be displaced when the graphical object is moved by the terminal user. Accordingly, the program should respond to this. But the program should either know that the tracked line value has changed or constantly survey graphical object property values, which is a resource-intense action. This fact is clearly stated by the help.

It is much more convenient to have functionality that signals about any change in any graphical object. Such a functionality is present in the terminal in the form of the OnChartEvent() event handler. Let's supplement its toolkit with several events, which clarify in our library what exactly happened with a graphical object. A library-controlled program will be able to know exactly what has happened with a graphical object and which property has been changed.

Let's divide the development of the functionality into two stages — first, create general events that may happen to graphical objects. Next, add the functionality clarifying what exactly happened to the object and allowing users to quickly find that out programmatically. Almost everything is ready for creating such a functionality. All we have to do is slightly improve the library classes and create a place in the event handler, in which events occurring to graphical objects are handled.

The basic graphical object events are defined as follows:

  • Creating a new graphical object,
  • Changing graphical object properties,
  • Renaming a graphical object,
  • Removing a graphical object,
  • Removing a graphical object together with the chart window.

These are the events I will implement in the current article. They are to be sent to the OnChartEvent() handler. In the next article, I will implement handling each of the events so that we can know exactly which properties have been changed on the graphical object.

Note that renaming a graphical object is, in turn, entails a change in the Name property. But I decided to put it in a separate event to simplify handling since changing the name causes several events in a row — removing a graphical object, creating a new one and changing its properties. All these states have already been handled in the library. The renaming event is calculated correctly. Therefore, we will send it for further programmatic handling of the event.

Thus, deleting a graphical object currently allows us to find out exactly which object was deleted and report its name. However, when deleting a chart window that contained graphical objects, we can still only know a number of graphical objects located on the deleted chart. For now, these two events will only inform us about the deletion of graphical objects. Perhaps, in the future, I will consider the need to remember the properties of deleted objects. For now, I do not see an urgent need to implement this feature.


Improving library classes

To track the graphical object allocation, we need to add a handler for a graphical object click event. Double-clicking on an unselected graphical object selects it for editing, while double-clicking on a selected object deselects it. To handle such an object event on the current chart, we need to add a new event ID to the event handler. In order to handle such an event on other charts, we need to improve the event control indicator automatically set on each newly opened chart window.

Open the indicator file \MQL5\Indicators\DoEasy\EventControl.mq5 and add a new event ID to be sent to the control program chart:

//+------------------------------------------------------------------+
//|                                                 EventControl.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0
//--- input parameters
input long     InpChartSRC = 0;
input long     InpChartDST = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator shortname
   IndicatorSetString(INDICATOR_SHORTNAME,"EventSend_From#"+(string)InpChartSRC+"_To#"+(string)InpChartDST);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
   return rates_total;
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK)
     {
      EventChartCustom(InpChartDST,(ushort)id,InpChartSRC,dparam,sparam);
     }
  }
//+------------------------------------------------------------------+


In \MQL5\Include\DoEasy\Data.mqh, add the new message indices:

//--- CGraphElementsCollection
   MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST,           // Failed to get the list of newly added objects
   MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST,         // Failed to remove a graphical object from the list
   
   MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR,           // Indicator for controlling and sending events created
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR,    // Failed to create the indicator for controlling and sending events
   MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR,            // Indicator for controlling and sending events successfully added to the chart
   MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR,       // Failed to add the indicator for controlling and sending events
   MSG_GRAPH_OBJ_CLOSED_CHARTS,                       // Chart windows closed:
   MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS,            // Objects removed together with charts:
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_OBJ,               // Failed to create the event object for a graphical object
   MSG_GRAPH_OBJ_FAILED_ADD_EVN_OBJ,                  // Failed to add the event object to the list
   
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE,                // New graphical object created
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE,                // Changed the graphical object property
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME,                // Graphical object renamed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE,                // Graphical object removed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART,             // Graphical object removed together with the chart
   
  };
//+------------------------------------------------------------------+

and the message texts corresponding to the newly added indices:

//--- CGraphElementsCollection
   {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"},
   {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
   
   {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"},
   {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"},
   {"Индикатор контроля и отправки событий успешно добавлен на график ","The indicator for monitoring and sending events has been successfully added to the chart"},
   {"Не удалось добавить индикатор контроля и отправки событий на график ","Failed to add the indicator of monitoring and sending events to the chart "},
   {"Закрыто окон графиков: ","Closed chart windows: "},
   {"С ними удалено объектов: ","Objects removed with them: "},
   {"Не удалось создать объект-событие для графического объекта","Failed to create event object for graphic object"},
   {"Не удалось добавить объект-событие в список","Failed to add event object to list"},
   
   {"Создан новый графический объект","New graphic object created"},
   {"Изменено свойство графического объекта","Changed graphic object property"},
   {"Графический объект переименован","Graphic object renamed"},
   {"Графический объект удалён","Graphic object deleted"},
   {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"},
   
  };
//+---------------------------------------------------------------------+


Let's make corrections in \MQL5\Include\DoEasy\Defines.mqh.

Remove the object relocation event from the list of possible graphical object events since this property change is already present in the enumeration of graphical object events:

//+------------------------------------------------------------------+
//| List of possible graphical object events                         |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_EVENT
  {
   GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// No event
   GRAPH_OBJ_EVENT_CREATE,                            // "Creating a new graphical object" event
   GRAPH_OBJ_EVENT_CHANGE,                            // "Changing graphical object properties" event
   GRAPH_OBJ_EVENT_MOVE,                              // "Moving graphical object" event
   GRAPH_OBJ_EVENT_RENAME,                            // "Renaming graphical object" event
   GRAPH_OBJ_EVENT_DELETE,                            // "Removing graphical object" event
  };

Add a new constant to the list for the event of removing a graphical object together with a chart:

//+------------------------------------------------------------------+
//| Data for handling graphical elements                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of possible graphical object events                         |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_EVENT
  {
   GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// No event
   GRAPH_OBJ_EVENT_CREATE,                            // "Creating a new graphical object" event
   GRAPH_OBJ_EVENT_CHANGE,                            // "Changing graphical object properties" event
   GRAPH_OBJ_EVENT_RENAME,                            // "Renaming graphical object" event
   GRAPH_OBJ_EVENT_DELETE,                            // "Removing graphical object" event
   GRAPH_OBJ_EVENT_DEL_CHART,                         // "Removing a graphical object together with the chart window" event
  };
#define GRAPH_OBJ_EVENTS_NEXT_CODE  (GRAPH_OBJ_EVENT_DEL_CHART+1) // The code of the next event after the last graphical object event code
//+------------------------------------------------------------------+

The code of the next event after the codes of the graphical object events is now calculated from the last value added to the enumeration.


When creating any standard graphical object, in its constructor, we read data from the physical object on the chart and write it to the class object properties. Some of the graphical object properties are set as common for all graphical objects. They can be found in each object and are set in the base class of all library graphical objects.

The algorithm is as follows: parameters are first read from a graphical object using standard ObjectGetXXX functions (Integer, Double and String). If the data is received successfully, these parameters are first set in the property of the CGBaseObj base graphical object class object. After that, they are set in its descendant properties. In case of standard graphical objects, this is the CGStdGraphObj class.

Here we face a certain conflict. We have the methods for setting graphical object parameters. These methods are to set the property value passed to them to the graphical object itself and to the appropriate class object property — in case the parameter is successfully set for the graphical object using the ObjectSetXXX functions (Integer, Double and String). But sometimes we simply need to set an already known value of a graphical object parameter to a class property. To achieve this, we do not need to read this value again, nor do we need to assign it to the graphical object. Instead, we should simply assign its value to the class variable. The Set methods of the base graphical object of the library first set the value to the graphical object parameters writing them to the class variables afterwards. To avoid setting the already known graphical object property and, instead, set it to the class variable, add the bool variable only_prop to such methods. The variable indicates the necessity to set the value either to the variable values only, or to the graphical object parameters, or to the class object properties. If the input is true, the parameters are first set only in the class variables. Otherwise, the value is first set to the graphical object. The class variables come second.

In the base object class of all library graphical objects \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, add the variable to such methods and change the method logic:

//--- Set the "Background object" flag
   bool              SetFlagBack(const bool flag,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop)
                          {
                           this.m_back=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }

Here, if the variable is false and the property is set for the graphical object, or if the variable is true, write the value passed to the method into the class variable.

All class methods modified this way:

//--- Set the "Background object" flag
   bool              SetFlagBack(const bool flag,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop)
                          {
                           this.m_back=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set the "Object selection" flag
   bool              SetFlagSelected(const bool flag,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) || only_prop)
                          {
                           this.m_selected=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set the "Object selection" flag
   bool              SetFlagSelectable(const bool flag,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop)
                          {
                           this.m_selectable=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag
   bool              SetFlagHidden(const bool flag,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop)
                          {
                           this.m_hidden=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set the priority of a graphical object for receiving the event of clicking on a chart 
   bool              SetZorder(const long value,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value)) || only_prop)
                          {
                           this.m_zorder=value;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set object visibility on all timeframes
   bool              SetVisible(const bool flag,const bool only_prop)   
                       {
                        long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) || only_prop)
                          {
                           this.m_visible=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Set visibility flags on timeframes specified as flags
   bool              SetVisibleOnTimeframes(const int flags,const bool only_prop)
                       {
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop)
                          {
                           this.m_timeframes_visible=flags;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Add the visibility flag on a specified timeframe
   bool              SetVisibleOnTimeframe(const ENUM_TIMEFRAMES timeframe,const bool only_prop)
                       {
                        int flags=this.m_timeframes_visible;
                        switch(timeframe)
                          {
                           case PERIOD_M1    : flags |= OBJ_PERIOD_M1;  break;
                           case PERIOD_M2    : flags |= OBJ_PERIOD_M2;  break;
                           case PERIOD_M3    : flags |= OBJ_PERIOD_M3;  break;
                           case PERIOD_M4    : flags |= OBJ_PERIOD_M4;  break;
                           case PERIOD_M5    : flags |= OBJ_PERIOD_M5;  break;
                           case PERIOD_M6    : flags |= OBJ_PERIOD_M6;  break;
                           case PERIOD_M10   : flags |= OBJ_PERIOD_M10; break;
                           case PERIOD_M12   : flags |= OBJ_PERIOD_M12; break;
                           case PERIOD_M15   : flags |= OBJ_PERIOD_M15; break;
                           case PERIOD_M20   : flags |= OBJ_PERIOD_M20; break;
                           case PERIOD_M30   : flags |= OBJ_PERIOD_M30; break;
                           case PERIOD_H1    : flags |= OBJ_PERIOD_H1;  break;
                           case PERIOD_H2    : flags |= OBJ_PERIOD_H2;  break;
                           case PERIOD_H3    : flags |= OBJ_PERIOD_H3;  break;
                           case PERIOD_H4    : flags |= OBJ_PERIOD_H4;  break;
                           case PERIOD_H6    : flags |= OBJ_PERIOD_H6;  break;
                           case PERIOD_H8    : flags |= OBJ_PERIOD_H8;  break;
                           case PERIOD_H12   : flags |= OBJ_PERIOD_H12; break;
                           case PERIOD_D1    : flags |= OBJ_PERIOD_D1;  break;
                           case PERIOD_W1    : flags |= OBJ_PERIOD_W1;  break;
                           case PERIOD_MN1   : flags |= OBJ_PERIOD_MN1; break;
                           default           : return true;
                          }
                        ::ResetLastError();
                        if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop)
                          {
                           this.m_timeframes_visible=flags;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }

Now we are able to select how to write the property — either we set a new property for the graphical object and the class variable (so that it corresponds to a newly set value), or we set the already known graphical object parameter value for the class variable (as it is done in the class constructor when creating a new graphical object). This allows us to avoid excessive access to rather slow functions for handling graphical objects due to their synchronism.

In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, fix the call of improved methods:

//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color
   int               ID(void)                            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID);                   }
   int               Number(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                  }
   bool              IsShadow(void)                      const { return this.m_shadow;                                                 }
   color             ChartColorBackground(void)          const { return this.m_chart_color_bg;                                         }
//--- Set the object above all
   void              BringToTop(void)                          { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);}
//--- (1) Show and (2) hide the element
   virtual void      Show(void)                                { CGBaseObj::SetVisible(true,false);                                    }
   virtual void      Hide(void)                                { CGBaseObj::SetVisible(false,false);                                   }
   
//+------------------------------------------------------------------+
//| The methods of receiving raster data                             |
//+------------------------------------------------------------------+

In \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, fix two methods:

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadowObj(const color colour,const uchar opacity)
  {
//--- If the shadow flag is disabled or the shadow object already exists, exit
   if(!this.m_shadow || this.m_shadow_obj!=NULL)
      return;

//--- Calculate the shadow object coordinates according to the offset from the top and left
   int x=this.CoordX()-OUTER_AREA_SIZE;
   int y=this.CoordY()-OUTER_AREA_SIZE;
//--- Calculate the width and height in accordance with the top, bottom, left and right offsets
   int w=this.Width()+OUTER_AREA_SIZE*2;
   int h=this.Height()+OUTER_AREA_SIZE*2;
//--- Create a new shadow object and set the pointer to it in the variable
   this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.CreateNameDependentObject("Shadow"),x,y,w,h);
   if(this.m_shadow_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ));
      return;
     }
//--- Set the properties for the created shadow object
   this.m_shadow_obj.SetID(this.ID());
   this.m_shadow_obj.SetNumber(-1);
   this.m_shadow_obj.SetOpacityShadow(opacity);
   this.m_shadow_obj.SetColorShadow(colour);
   this.m_shadow_obj.SetMovable(true);
   this.m_shadow_obj.SetActive(false);
   this.m_shadow_obj.SetVisible(false,false);
//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+
//| Draw the shadow                                                  |
//+------------------------------------------------------------------+
void CForm::DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=4)
  {
//--- If the shadow flag is disabled, exit
   if(!this.m_shadow)
      return;
//--- If there is no shadow object, create it
   if(this.m_shadow_obj==NULL)
      this.CreateShadowObj(colour,opacity);
//--- If the shadow object exists, draw the shadow on it,
//--- set the shadow object visibility flag and
//--- move the form object to the foreground
   if(this.m_shadow_obj!=NULL)
     {
      this.m_shadow_obj.DrawShadow(shift_x,shift_y,blur);
      this.m_shadow_obj.SetVisible(true,false);
      this.BringToTop();
     }
  }
//+------------------------------------------------------------------+


In the standard graphical object class, make all methods setting graphical object properties to have a boolean type of a return value (currently, the methods return no value and have the void type). This is necessary so that the appropriate parent class methods also having the bool return type are reassigned in the child class. In this case, we are able to avoid confusion when writing the code since the hint does not contain two methods of the bool type from the base object and of the void type from its descendant.

Let's consider this using the method for setting the subwindow index as as an example:

//--- Chart subwindow index
   int               SubWindow(void)               const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WND_NUM,0);                        }
   bool              SetSubWindow(void)
                       {
                        if(!CGBaseObj::SetSubwindow(CGBaseObj::ChartID(),CGBaseObj::Name()))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow());
                        return true;
                       }

Since a subwindow index is searched for by a chart ID and object name, the chart ID already set in the base object and the graphical object name are simply passed to the base class method for setting the subwindow index. If failed to set the value in the base object, return false, otherwise set the value newly set in the base object to the object property and return true.

Let's consider this using the method for setting the object visibility on all timeframes as an example:

//--- Object visibility on timeframes
   bool              Visible(void)                 const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0);                    }
   bool              SetFlagVisible(const bool flag,const bool only_prop)
                       {
                        if(!CGBaseObj::SetVisible(flag,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,flag);
                        return true;
                       }

Here the method receives a new parameter I have previously added in order to specify the values the parameter is to be set to — the object property only or the graphical object parameters and the class property. Next, set these properties to the base object. If failed, return false.
Otherwise, write the value to the class object property and return true.

All other methods are modified identically to the ones considered above:

//--- Background object
   bool              Back(void)                    const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);                          }
   bool              SetFlagBack(const bool flag,const bool only_prop)
                       {
                        if(!CGBaseObj::SetFlagBack(flag,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag);
                        return true;
                       }
//--- Priority of a graphical object for receiving the event of clicking on a chart
   long              Zorder(void)                  const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0);                              }
   bool              SetZorder(const long value,const bool only_prop)
                       {
                        if(!CGBaseObj::SetZorder(value,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value);
                        return true;
                       }
//--- Disable displaying the name of a graphical object in the terminal object list
   bool              Hidden(void)                  const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);                        }
   bool              SetFlagHidden(const bool flag,const bool only_prop)
                       {
                        if(!CGBaseObj::SetFlagHidden(flag,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag);
                        return true;
                       }
//--- Object selection
   bool              Selected(void)                const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);                      }
   bool              SetFlagSelected(const bool flag,const bool only_prop)
                       {
                        if(!CGBaseObj::SetFlagSelected(flag,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,flag);
                        return true;
                       }
//--- Object availability
   bool              Selectable(void)              const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);                    }
   bool              SetFlagSelectable(const bool flag,const bool only_prop)
                       {
                        if(!CGBaseObj::SetFlagSelectable(flag,only_prop))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,flag);
                        return true;
                       }
//--- Time coordinate
   datetime          Time(const int modifier)      const { return (datetime)this.GetProperty(GRAPH_OBJ_PROP_TIME,modifier);               }
   bool              SetTime(const datetime time,const int modifier)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TIME,modifier,time))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_TIME,modifier,time);
                        return true;
                       }
//--- Color
   color             Color(void)                   const { return (color)this.GetProperty(GRAPH_OBJ_PROP_COLOR,0);                        }
   bool              SetColor(const color colour)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_COLOR,colour))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,colour);
                        return true;
                       }
//--- Style
   ENUM_LINE_STYLE   Style(void)                   const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_STYLE,0);              }
   bool              SetStyle(const ENUM_LINE_STYLE style)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STYLE,style))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,style);
                        return true;
                       }
//--- Line width
   int               Width(void)                   const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WIDTH,0);                          }
   bool              SetWidth(const int width)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_WIDTH,width))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,width);
                        return true;
                       }
//--- Object color filling
   bool              Fill(void)                    const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_FILL,0);                          }
   bool              SetFlagFill(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FILL,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_FILL,0,flag);
                        return true;
                       }
//--- Ability to edit text in the Edit object
   bool              ReadOnly(void)                const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_READONLY,0);                      }
   bool              SetFlagReadOnly(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_READONLY,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,flag);
                        return true;
                       }
//--- Number of levels
   int               Levels(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0);                         }
   bool              SetLevels(const int levels)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELS,levels))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,levels);
                        return true;
                       }
//--- Line level color
   color             LevelColor(const int modifier)   const { return (color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier);         }
   bool              SetLevelColor(const color colour,const int modifier)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELCOLOR,modifier,colour))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier,colour);
                        return true;
                       }
//--- Level line style
   ENUM_LINE_STYLE   LevelStyle(const int modifier)const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier);  }
   bool              SetLevelStyle(const ENUM_LINE_STYLE style,const int modifier)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELSTYLE,modifier,style))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier,style);
                        return true;
                       }
///--- Level line width
   int               LevelWidth(const int modifier)const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier);              }
   bool              SetLevelWidth(const int width,const int modifier)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELWIDTH,modifier,width))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier,width);
                        return true;
                       }
//--- Horizontal text alignment in the Edit object (OBJ_EDIT)
   ENUM_ALIGN_MODE   Align(void)                   const { return (ENUM_ALIGN_MODE)this.GetProperty(GRAPH_OBJ_PROP_ALIGN,0);              }
   bool              SetAlign(const ENUM_ALIGN_MODE align)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ALIGN,align))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,align);
                        return true;
                       }
//--- Font size
   int               FontSize(void)                const { return (int)this.GetProperty(GRAPH_OBJ_PROP_FONTSIZE,0);                       }
   bool              SetFontSize(const int size)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONTSIZE,size))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,size);
                        return true;
                       }
//--- Ray goes to the left
   bool              RayLeft(void)                 const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0);                      }
   bool              SetFlagRayLeft(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_LEFT,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,flag);
                        return true;
                       }
//--- Ray goes to the right
   bool              RayRight(void)                const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0);                     }
   bool              SetFlagRayRight(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_RIGHT,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,flag);
                        return true;
                       }
//--- Vertical line goes through all windows of a chart
   bool              Ray(void)                     const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY,0);                           }
   bool              SetFlagRay(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_RAY,0,flag);
                        return true;
                       }
//--- Display the full ellipse of the Fibonacci Arc object
   bool              Ellipse(void)                 const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_ELLIPSE,0);                       }
   bool              SetFlagEllipse(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ELLIPSE,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,flag);
                        return true;
                       }
//--- Arrow code for the "Arrow" object
   uchar             ArrowCode(void)               const { return (uchar)this.GetProperty(GRAPH_OBJ_PROP_ARROWCODE,0);                    }
   bool              SetArrowCode(const uchar code)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ARROWCODE,code))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,code);
                        return true;
                       }
//--- Position of the graphical object anchor point
   int               Anchor(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0);                         }
   bool              SetAnchor(const int anchor)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANCHOR,anchor))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,anchor);
                        return true;
                       }
//--- Distance from the base corner along the X axis in pixels
   int               XDistance(void)               const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XDISTANCE,0);                      }
   bool              SetXDistance(const int distance)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XDISTANCE,distance))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,distance);
                        return true;
                       }
//--- Distance from the base corner along the Y axis in pixels
   int               YDistance(void)               const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YDISTANCE,0);                      }
   bool              SetYDistance(const int distance)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YDISTANCE,distance))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,distance);
                        return true;
                       }
//--- Gann object trend
   ENUM_GANN_DIRECTION Direction(void)             const { return (ENUM_GANN_DIRECTION)this.GetProperty(GRAPH_OBJ_PROP_DIRECTION,0);      }
   bool              SetDirection(const ENUM_GANN_DIRECTION direction)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DIRECTION,direction))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,direction);
                        return true;
                       }
//--- Elliott wave marking level
   ENUM_ELLIOT_WAVE_DEGREE Degree(void)            const { return (ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(GRAPH_OBJ_PROP_DEGREE,0);     }
   bool              SetDegree(const ENUM_ELLIOT_WAVE_DEGREE degree)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEGREE,degree))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,degree);
                        return true;
                       }
//--- Display lines for Elliott wave marking
   bool              DrawLines(void)               const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_DRAWLINES,0);                     }
   bool              SetFlagDrawLines(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DRAWLINES,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,flag);
                        return true;
                       }
//--- Button state (pressed/released)
   bool              State(void)                   const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_STATE,0);                         }
   bool              SetFlagState(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STATE,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_STATE,0,flag);
                        return true;
                       }
//--- Chart object ID (OBJ_CHART)
   long              ChartObjChartID(void)         const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0);                  }
   bool              SetChartObjChartID(const long chart_id)
                       {
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,chart_id);
                        return true;
                       }
//--- Chart object period
   ENUM_TIMEFRAMES   ChartObjPeriod(void)          const { return (ENUM_TIMEFRAMES)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0);   }
   bool              SetChartObjPeriod(const ENUM_TIMEFRAMES timeframe)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PERIOD,timeframe))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,timeframe);
                        return true;
                       }
//--- Time scale display flag for the Chart object
   bool              ChartObjDateScale(void)       const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0);          }
   bool              SetFlagChartObjDateScale(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DATE_SCALE,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,flag);
                        return true;
                       }
//--- Price scale display flag for the Chart object
   bool              ChartObjPriceScale(void)      const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0);         }
   bool              SetFlagPriceScale(const bool flag)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE_SCALE,flag))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,flag);
                        return true;
                       }
//--- Chart object scale
   int               ChartObjChartScale(void)      const { return (int)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0);          }
   bool              SetChartObjChartScale(const int scale)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CHART_SCALE,scale))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,scale);
                        return true;
                       }
//--- Object width along the X axis in pixels
   int               XSize(void)                   const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XSIZE,0);                          }
   bool              SetXSize(const int size)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XSIZE,size))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,size);
                        return true;
                       }
//--- Object height along the Y axis in pixels
   int               YSize(void)                   const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YSIZE,0);                          }
   bool              SetYSize(const int size)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YSIZE,size))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,size);
                        return true;
                       }
//--- X coordinate of the upper-left corner of the visibility area
   int               XOffset(void)                 const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XOFFSET,0);                        }
   bool              SetXOffset(const int offset)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XOFFSET,offset))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,offset);
                        return true;
                       }
//--- Y coordinate of the upper-left corner of the visibility area
   int               YOffset(void)                 const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YOFFSET,0);                        }
   bool              SetYOffset(const int offset)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YOFFSET,offset))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,offset);
                        return true;
                       }
//--- Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   color             BGColor(void)                 const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BGCOLOR,0);                      }
   bool              SetBGColor(const color colour)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BGCOLOR,colour))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,colour);
                        return true;
                       }
//--- Chart corner for attaching a graphical object
   ENUM_BASE_CORNER  Corner(void)                  const { return (ENUM_BASE_CORNER)this.GetProperty(GRAPH_OBJ_PROP_CORNER,0);            }
   bool              SetCorner(const ENUM_BASE_CORNER corner)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CORNER,corner))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,corner);
                        return true;
                       }
//--- Border type for the Rectangle label object
   ENUM_BORDER_TYPE  BorderType(void)              const { return (ENUM_BORDER_TYPE)this.GetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0);       }
   bool              SetBorderType(const ENUM_BORDER_TYPE type)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_TYPE,type))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,type);
                        return true;
                       }
//--- Border color for OBJ_EDIT and OBJ_BUTTON
   color             BorderColor(void)             const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0);                 }
   bool              SetBorderColor(const color colour)
                       {
                        if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_COLOR,colour))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,colour);
                        return true;
                       }

//--- Price coordinate
   double            Price(const int modifier)     const { return this.GetProperty(GRAPH_OBJ_PROP_PRICE,modifier);                        }
   bool              SetPrice(const double price,const int modifier)
                       {
                        if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE,modifier,price))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_PRICE,modifier,price);
                        return true;
                       }
//--- Level value
   double            LevelValue(const int modifier)const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier);                   }
   bool              SetLevelValue(const double value,const int modifier)
                       {
                        if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELVALUE,modifier,value))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier,value);
                        return true;
                       }
//--- Scale
   double            Scale(void)                   const { return this.GetProperty(GRAPH_OBJ_PROP_SCALE,0);                               }
   bool              SetScale(const double scale)
                       {
                        if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SCALE,scale))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_SCALE,0,scale);
                        return true;
                       }
//--- Angle
   double            Angle(void)                   const { return this.GetProperty(GRAPH_OBJ_PROP_ANGLE,0);                               }
   bool              SetAngle(const double angle)
                       {
                        if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANGLE,angle))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_ANGLE,0,angle);
                        return true;
                       }
//--- Deviation of the standard deviation channel
   double            Deviation(void)               const { return this.GetProperty(GRAPH_OBJ_PROP_DEVIATION,0);                           }
   bool              SetDeviation(const double deviation)
                       {
                        if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEVIATION,deviation))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,0,deviation);
                        return true;
                       }

//--- Object name
   string            Name(void)                    const { return this.GetProperty(GRAPH_OBJ_PROP_NAME,0);                                }
   bool              SetName(const string name)
                       {
                        if(CGBaseObj::Name()==name)
                           return true;
                        if(CGBaseObj::Name()=="")
                          {
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                        else
                          {
                           if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name))
                              return false;
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                       }
//--- Object description (text contained in the object)
   string            Text(void)                    const { return this.GetProperty(GRAPH_OBJ_PROP_TEXT,0);                                }
   bool              SetText(const string text)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TEXT,text))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_TEXT,0,text);
                        return true;
                       }
//--- Tooltip text
   string            Tooltip(void)                 const { return this.GetProperty(GRAPH_OBJ_PROP_TOOLTIP,0);                             }
   bool              SetTooltip(const string tooltip)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TOOLTIP,tooltip))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,0,tooltip);
                        return true;
                       }
//--- Level description
   string            LevelText(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier);                    }
   bool              SetLevelText(const string text,const int modifier)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELTEXT,modifier,text))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier,text);
                        return true;
                       }
//--- Font
   string            Font(void)                    const { return this.GetProperty(GRAPH_OBJ_PROP_FONT,0);                                }
   bool              SetFont(const string font)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONT,font))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_FONT,0,font);
                        return true;
                       }
//--- BMP file name for the "Bitmap Level" object
   string            BMPFile(const int modifier)   const { return this.GetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier);                      }
   bool              SetBMPFile(const string bmp_file,const int modifier)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BMPFILE,bmp_file))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier,bmp_file);
                        return true;
                       }
//--- Symbol for the Chart object 
   string            ChartObjSymbol(void)          const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0);                    }
   bool              SetChartObjSymbol(const string symbol)
                       {
                        if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SYMBOL,symbol))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,symbol);
                        return true;
                       }

In the method receiving and saving integer properties, first write the data present in the base object to the base object properties. Next, write the values from that object to the graphical object class properties:

//+------------------------------------------------------------------+
//| Get and save the integer properties                              |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveINT(void)
  {
   //--- Properties inherent in all graphical objects and present in a graphical object
   CGBaseObj::SetVisibleOnTimeframes((int)::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES),true);   // Write Object visibility on timeframes to the base object
   CGBaseObj::SetFlagBack(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK),true);                         // Write Background object flag to the base object
   CGBaseObj::SetZorder(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER),true);                         // Write Priority of a graphical object for receiving the event of clicking on a chart to the base object
   CGBaseObj::SetFlagHidden(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN),true);                     // Write Disable displaying the name of a graphical object in the terminal object list to the base object
   CGBaseObj::SetFlagSelectable(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE),true);             // Write Object availability to the base object
   CGBaseObj::SetFlagSelected(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED),true);                 // Write Object selection to the base object
   
   this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,CGBaseObj::VisibleOnTimeframes());                                   // Object visibility on timeframes
   this.SetProperty(GRAPH_OBJ_PROP_BACK,0,CGBaseObj::IsBack());                                                      // Background object
   this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,CGBaseObj::Zorder());                                                    // Priority of a graphical object for receiving the event of clicking on a chart
   this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,CGBaseObj::IsHidden());                                                  // Disable displaying the name of a graphical object in the terminal object list
   this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,CGBaseObj::IsSelectable());                                          // Object availability
   this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,CGBaseObj::IsSelected());                                              // Object selection
   
   this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME));  // Object creation time
   for(int i=0;i<this.m_pivots;i++)                                                                                  // Point time coordinates
      this.SetTimePivot(i);
   this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_COLOR));            // Color
   this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STYLE));            // Style
   this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_WIDTH));            // Line width
   //--- Properties belonging to different graphical objects
   this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL));              // Fill an object with color
   this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY));      // Ability to edit text in the Edit object
   this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS));          // Number of levels
   
   if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0))                      // Check if the number of levels has changed
     {
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,this.Levels());
     }
   for(int i=0;i<this.Levels();i++)                                                                                  // Level data
     {
      this.SetLevelColor(i);
      this.SetLevelStyle(i);
      this.SetLevelWidth(i);
     }
   this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN));            // Horizontal text alignment in the Edit object (OBJ_EDIT)
   this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FONTSIZE));      // Font size
   this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_LEFT));      // Ray goes to the left
   this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_RIGHT));    // Ray goes to the right
   this.SetProperty(GRAPH_OBJ_PROP_RAY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY));                // Vertical line goes through all windows of a chart
   this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ELLIPSE));        // Display the full ellipse of the Fibonacci Arc object
   this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ARROWCODE));    // Arrow code for the "Arrow" object
   this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ANCHOR));          // Position of the binding point of the graphical object
   this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XDISTANCE));    // Distance from the base corner along the X axis in pixels
   this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YDISTANCE));    // Distance from the base corner along the Y axis in pixels
   this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DIRECTION));    // Gann object trend
   this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DEGREE));          // Elliott wave marking level
   this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DRAWLINES));    // Display lines for Elliott wave marking
   this.SetProperty(GRAPH_OBJ_PROP_STATE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STATE));            // Button state (pressed/released)
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_ID));// Chart object ID (OBJ_CHART).
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PERIOD));    // Chart object period
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DATE_SCALE));  // Time scale display flag for the Chart object
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PRICE_SCALE));// Price scale display flag for the Chart object
   this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_SCALE));// Chart object scale
   this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XSIZE));            // Object width along the X axis in pixels.
   this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YSIZE));            // Object height along the Y axis in pixels.
   this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XOFFSET));        // X coordinate of the upper-left corner of the visibility area.
   this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YOFFSET));        // Y coordinate of the upper-left corner of the visibility area.
   this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BGCOLOR));        // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CORNER));          // Chart corner for binding a graphical object
   this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_TYPE));// Border type for "Rectangle border"
   this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_COLOR));// Border color for OBJ_EDIT and OBJ_BUTTON
  }
//+------------------------------------------------------------------+

Thus, we fill in all the properties duplicated in the base class and its descendant. Otherwise, when receiving object properties, we use the base object methods. No graphical object data has been written in them.

I will leave some improvements for consideration when creating the functionality for tracking standard graphical object events.


Standard graphical object events

The logic of handling graphical object events is to be based on the following concept: we are able to define any events occurring to object properties. Let's create a small class of the graphical object base event. When defining any graphical object event, a new event object will be created which is to receive:

  • Event ID,
  • ID of the chart containing a graphical object an event has occurred with,
  • Name of the object an event has occurred at.

A custom event sent to a specified chart by the EventChartCustom() function has five parameters:

//+------------------------------------------------------------------+
bool  EventChartCustom(
   long    chart_id,            // event receiving chart ID
   ushort  custom_event_id,     // event ID
   long    lparam,              // long parameter
   double  dparam,              // double parameter
   string  sparam               // event string parameter
   );
//+------------------------------------------------------------------+

The IDs to be filled in are custom_event_id for graphical object event ID, lparam for ID of the chart containing a graphical object an event has occurred with and sparam for a graphical object name. We also have one free parameter dparam. We will use it to specify the value of the constant of a changed property when it is changed or the number of graphical objects removed together with a closed chart window.

An event object is to be created (and set in the event list) for each object event. After checking object properties for changes, we will have the complete list of all events occurred to a graphical object. Upon completion of the verification of all object properties, we will move along the newly created event list sending each event to the control program chart where the OnChartEvent() event handler method of the graphical element collection class will be called in its turn. The handler allows processing each event sent from the created list of graphical object events. Accordingly, each such event will contain the event ID, the ID of the chart a modified graphical object is located on and its name. This will precisely define the graphical object, while the knowledge of the changed property constant accurately points out to a changed property so that we are able to get the pointer to the object in the collection and read a new value of the changed property. All this will allow us to accurately point both to the graphical object itself and to the changed property in order to further handle the event in the program according to its inherent logic.

In the current article, I will only implement creating the events and sending them to the handler of graphical object collection events. These events will be decomposed to their components in the next article.

At the very start of the base graphical object file \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, add the new class of the base event of the graphical library objects:

//+------------------------------------------------------------------+
//|                                                     GBaseObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include <Graphics\Graphic.mqh>
//+------------------------------------------------------------------+
//| Graphical object event class                                     |
//+------------------------------------------------------------------+
class CGBaseEvent : public CObject
  {
private:
   ushort            m_id;
   long              m_lparam;
   double            m_dparam;
   string            m_sparam;
public:
   void              ID(ushort id)                          { this.m_id=id;         }
   ushort            ID(void)                         const { return this.m_id;     }
   void              LParam(const long value)               { this.m_lparam=value;  }
   long              Lparam(void)                     const { return this.m_lparam; }
   void              DParam(const double value)             { this.m_dparam=value;  }
   double            Dparam(void)                     const { return this.m_dparam; }
   void              SParam(const string value)             { this.m_sparam=value;  }
   string            Sparam(void)                     const { return this.m_sparam; }
   bool              Send(const long chart_id)
                       {
                        ::ResetLastError();
                        return ::EventChartCustom(chart_id,m_id,m_lparam,m_dparam,m_sparam);
                       }
                     CGBaseEvent (const ushort event_id,const long lparam,const double dparam,const string sparam) : 
                        m_id(event_id),m_lparam(lparam),m_dparam(dparam),m_sparam(sparam){}
                    ~CGBaseEvent (void){}
  };
//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject

The class member variables for storing all event object properties described above are declared in the private section of the class, while the public section contains the methods for setting and returning the values of the variables and the method for sending a custom event to a specified chart.
The class constructor receives all the values to be assigned to the class variables. The values are immediately assigned to the variables in the constructor initialization list.

In the protected section of the base graphical object class, declare the list for storing object events and write the methods for handling the list and graphical object events:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
protected:
   CArrayObj         m_list_events;                      // Object event list
   ENUM_OBJECT       m_type_graph_obj;                   // Graphical object type
   ENUM_GRAPH_ELEMENT_TYPE m_type_element;               // Graphical element type
   ENUM_GRAPH_OBJ_BELONG m_belong;                       // Program affiliation
   ENUM_GRAPH_OBJ_GROUP m_group;                         // Graphical object group
   string            m_name_prefix;                      // Object name prefix
   string            m_name;                             // Object name
   long              m_chart_id;                         // Object chart ID
   long              m_object_id;                        // Object ID
   long              m_zorder;                           // Priority of a graphical object for receiving the mouse click event
   int               m_subwindow;                        // Subwindow index
   int               m_shift_y;                          // Subwindow Y coordinate shift
   int               m_type;                             // Object type
   int               m_timeframes_visible;               // Visibility of an object on timeframes (a set of flags)
   int               m_digits;                           // Number of decimal places in a quote
   bool              m_visible;                          // Object visibility
   bool              m_back;                             // "Background object" flag
   bool              m_selected;                         // "Object selection" flag
   bool              m_selectable;                       // "Object availability" flag
   bool              m_hidden;                           // "Disable displaying the name of a graphical object in the terminal object list" flag
   datetime          m_create_time;                      // Object creation time
   
//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void)                      { return true; }
   virtual void      StructToObject(void){;}

//--- Return the list of object events
   CArrayObj        *GetListEvents(void)                       { return &this.m_list_events;          }
//--- Create a new object event
   CGBaseEvent      *CreateNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam)
                       {
                        CGBaseEvent *event=new CGBaseEvent(event_id,lparam,dparam,sparam);
                        return event;
                       }
//--- Create a new object event and add it to the event list
   bool              CreateAndAddNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam)
                       {
                        return this.AddEvent(new CGBaseEvent(event_id,lparam,dparam,sparam));
                       }
//--- Add an event object to the event list
   bool              AddEvent(CGBaseEvent *event)              { return this.m_list_events.Add(event);}
//--- Clear the event list
   void              ClearEventsList(void)                     { this.m_list_events.Clear();          }
//--- Return the number of events in the list
   int               EventsTotal(void)                         { return this.m_list_events.Total();   }

public:

All provided methods are pretty simple and easy to understand. If you have any questions, feel free to ask them in the comments below.

In the class constructor, clear the list of object events and set the sorted list flag:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
   this.m_list_events.Clear();                  // Clear the event list
   this.m_list_events.Sort();                   // Sorted list flag
   this.m_type=OBJECT_DE_TYPE_GBASE;            // Object type
   this.m_type_graph_obj=WRONG_VALUE;           // Graphical object type
   this.m_type_element=WRONG_VALUE;             // Graphical object type
   this.m_belong=WRONG_VALUE;                   // Program/terminal affiliation
   this.m_name_prefix="";                       // Object name prefix
   this.m_name="";                              // Object name
   this.m_chart_id=0;                           // Object chart ID
   this.m_object_id=0;                          // Object ID
   this.m_zorder=0;                             // Priority of a graphical object for receiving the mouse click event
   this.m_subwindow=0;                          // Subwindow index
   this.m_shift_y=0;                            // Subwindow Y coordinate shift
   this.m_timeframes_visible=OBJ_ALL_PERIODS;   // Visibility of an object on timeframes (a set of flags)
   this.m_visible=true;                         // Object visibility
   this.m_back=false;                           // "Background object" flag
   this.m_selected=false;                       // "Object selection" flag
   this.m_selectable=false;                     // "Object availability" flag
   this.m_hidden=true;                          // "Disable displaying the name of a graphical object in the terminal object list" flag
   this.m_create_time=0;                        // Object creation time
   
  }
//+------------------------------------------------------------------+


In the standard graphical object class \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh, namely in the method checking the change in the object properties, clear the event list, set creating and adding event objects to the list and sending them to the handler of graphical object collection events from the created list:

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   CGBaseObj::ClearEventsList();
   bool changed=false;
   int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME)
           {
            changed=true;
            this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name());
           }
        }
     }
   if(changed)
     {
      for(int i=0;i<this.m_list_events.Total();i++)
        {
         CGBaseEvent *event=this.m_list_events.At(i);
         if(event==NULL)
            continue;
         ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam());
        }
      PropertiesCopyToPrevData();
     }
  }
//+------------------------------------------------------------------+

Here, we check if there are changes in each object property in the loop by all object properties. If a change has been detected, create an event object and add it to the list of object events. If there is at least one change, get each event object and send it to the control program chart in the loop by the created list. Next, these events are sent and handled in the OnChartEvent() handler of the graphical element collection class.

Open the file of the graphical element collection class \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh. Let's slightly improve the graphical object management class method checking chart objects:

//+------------------------------------------------------------------+
//| CChartObjectsControl: Check objects on a chart                   |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Graphical objects on the chart
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects;

   //--- If an object is added to the chart
   if(this.m_delta_graph_obj>0)
     {
      //--- find the last added graphical object, select it and write its name
      string name=this.LastAddedGraphObjName();
      //--- Handle only non-programmatically created objects
      if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE)
        {
         //--- Create the object of the graphical object class corresponding to the added graphical object type
         ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE);
         ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1);
         CGStdGraphObj *obj=this.CreateNewGraphObj(type,name);
         if(obj==NULL)
           {
            CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ);
            return;
           }
         //--- Set the object affiliation and add the created object to the list of new objects
         obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); 
         if(this.m_list_new_graph_obj.Search(obj)==WRONG_VALUE)
           {
            //--- If failed to add the object to the list, inform of that, delete the object and leave
            if(!this.m_list_new_graph_obj.Add(obj))
              {
               CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
               delete obj;
               return;
              }
            //--- Send an event to the control program chart
            ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),0,obj.Name());
           }
        }
     }

//--- save the index of the last added graphical object and the difference with the last check
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

Here I added displaying error messages to the journal and sending an object creation event to the control program chart.

In the public section of the graphical element collection class, add the method returning the chart management object list:

//--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects
   int               NewObjects(void)   const                                                            { return this.m_delta_graph_obj;       }
   bool              IsEvent(void) const                                                                 { return this.m_is_graph_obj_event;    }
//--- Return a graphical object by chart name and ID
   CGStdGraphObj    *GetStdGraphObject(const string name,const long chart_id);
//--- Return the chart management object list
   CArrayObj        *GetListChartsControl(void)                                                          { return &this.m_list_charts_control;  }
//--- Constructor
                     CGraphElementsCollection();


The methods for creating standard graphical objects created in the previous article feature a large repeating code chunk. Let's move it to a separate private method:

//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

private:
//--- Create a new graphical object, return the pointer to the chart management object
   CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id,
                                                              const string name,
                                                              int subwindow,
                                                              const ENUM_OBJECT type_object,
                                                              const datetime time1,
                                                              const double price1,
                                                              const datetime time2=0,
                                                              const double price2=0,
                                                              const datetime time3=0,
                                                              const double price3=0,
                                                              const datetime time4=0,
                                                              const double price4=0,
                                                              const datetime time5=0,
                                                              const double price5=0)
                       {
                        //--- If an object with a chart ID and name is already present in the collection, inform of that and return NULL
                        if(this.IsPresentGraphObjInList(chart_id,name))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name);
                           return NULL;
                          }
                        //--- If failed to create a new standard graphical object, inform of that and return NULL
                        if(!this.CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,0))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object));
                           CMessage::ToLog(::GetLastError(),true);
                           return NULL;
                          }
                        //--- If failed to get a chart management object, inform of that
                        CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id);
                        if(ctrl==NULL)
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id);
                        //--- Return the pointer to a chart management object or NULL in case of a failed attempt to get it
                        return ctrl;
                       }
//--- Add a newly created object to the list
   bool              AddCreatedObjToList(const string source,const long chart_id,const string name,CGStdGraphObj *obj)
                       {
                        bool res=true;
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,name);
                           delete obj;
                           res=false;
                          }
                        ::ChartRedraw(chart_id);
                        return res;
                       }
public:
//--- Create the "Vertical line" graphical object

In all methods for creating standard graphical objects, return the operation result of the method:

public:
//--- Create the "Vertical line" graphical object
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time)
                       {
                        //--- Set the name and type of a created object
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_VLINE;
                        //--- Create a new graphical object and get the pointer to the chart management object
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0);
                        if(ctrl==NULL)
                           return false;
                        //--- Create a new class object corresponding to the newly created graphical object
                        CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Set the necessary minimal parameters for an object
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true,false);
                        obj.SetFlagSelected(true,false);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Return the result of adding the object to the list
                        return this.AddCreatedObjToList(DFUN,chart_id,nm,obj);
                       }

Also, set the flags necessary for the correct operation of the newly changed method for setting the values of graphical object properties.

All other methods for creating standard graphical objects are changed the same way. There is no point in considering them here. You can find them in the attachments below.

In the method creating a new object of managing graphical objects of a specified chart and adding it to the list, implement adding messages to the journal when the event control indicator is added to the chart:

//+------------------------------------------------------------------+
//| Create a new graphical object management object                  |
//| for a specified and add it to the list                           |
//+------------------------------------------------------------------+
CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id)
  {
//--- Create a new object for managing chart objects by ID
   CChartObjectsControl *obj=new CChartObjectsControl(chart_id);
//--- If the object is not created, inform of the error and return NULL
   if(obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id);
      return NULL;
     }
//--- If failed to add the object to the list, inform of the error, remove the object and return NULL
   if(!this.m_list_charts_control.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
      delete obj;
      return NULL;
     }
   if(obj.ChartID()!=CBaseObj::GetMainChartID() && obj.CreateEventControlInd(CBaseObj::GetMainChartID()))
      if(!obj.AddEventControlInd())
        {
         CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR);
         CMessage::ToLog(DFUN,::GetLastError(),true);
        }
      else
         ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR),obj.Symbol()," #",obj.ChartID());
//--- Return the pointer to the object that was created and added to the list
   return obj;
  }
//+------------------------------------------------------------------+


In the method updating the list of all graphical objects, send a graphical object removal event instead of simply displaying in the journal the detected collection list object that has been removed:

//+------------------------------------------------------------------+
//| Update the list of all graphical objects                         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
   this.RefreshForExtraObjects();
//--- Declare variables to search for charts
   long chart_id=0;
   int i=0;
//--- In the loop by all open charts in the terminal (no more than 100)
   while(i<CHARTS_MAX)
     {
      //--- Get the chart ID
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Get the pointer to the object for managing graphical objects
      //--- and update the list of graphical objects by chart ID
      CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id);
      //--- If failed to get the pointer, move on to the next chart
      if(obj_ctrl==NULL)
         continue;
      //--- If the number of objects on the chart changes
      if(obj_ctrl.IsEvent())
        {
         //--- If a graphical object is added to the chart
         if(obj_ctrl.Delta()>0)
           {
            //--- Get the list of added graphical objects and move them to the collection list
            //--- (if failed to move the object to the collection, move on to the next object)
            if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl))
               continue;
           }
         //--- If the graphical object has been removed
         else if(obj_ctrl.Delta()<0)
           {
            // Find an extra object in the list
            CGStdGraphObj *obj=this.FindMissingObj(chart_id);
            if(obj!=NULL)
              {
               //--- Send an event to the control program chart
               ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,obj.ChartID(),0,obj.Name());
               //--- Remove the class object of a removed graphical object from the collection list
               if(!this.DeleteGraphObjFromList(obj))
                  CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
              }
           }
        }
      //--- Increase the loop index
      i++;
     }
  }
//+------------------------------------------------------------------+

In the event string parameter, pass the name of a removed object. Perhaps, I will implement saving removed objects to a separate list, so that when receiving a removal event, we are able to define a removed object and its properties, as well as restore it programmatically.

In the method handling a chart window removal, send an event to the control program chart as well instead of a simple journal message:

//+------------------------------------------------------------------+
//| Handle removing the chart window                                 |
//+------------------------------------------------------------------+
void CGraphElementsCollection::RefreshForExtraObjects(void)
  {
   for(int i=this.m_list_charts_control.Total()-1;i>WRONG_VALUE;i--)
     {
      CChartObjectsControl *obj_ctrl=this.m_list_charts_control.At(i);
      if(obj_ctrl==NULL)
         continue;
      if(!this.IsPresentChartWindow(obj_ctrl.ChartID()))
        {
         long chart_id=obj_ctrl.ChartID();
         string chart_symb=obj_ctrl.Symbol();
         int total_ctrl=this.m_list_charts_control.Total();
         this.DeleteGraphObjCtrlObjFromList(obj_ctrl);
         int total_obj=this.m_list_all_graph_obj.Total();
         this.DeleteGraphObjectsFromList(chart_id);
         int del_ctrl=total_ctrl-this.m_list_charts_control.Total();
         int del_obj=total_obj-this.m_list_all_graph_obj.Total();
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DEL_CHART,chart_id,del_obj,chart_symb);
        }
     }
  }
//+------------------------------------------------------------------+

Here we pass the closed chart ID, the number of graphical objects removed together with that chart and the closed chart symbol in the form of parameters.

In the handler of the graphical element collection class events, add handling base events of graphical objects:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj=NULL;
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || id==CHARTEVENT_OBJECT_CLICK   ||
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Calculate the chart ID
      //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID
      //--- If the event ID corresponds to a user event, the chart ID is received from lparam
      //--- Otherwise, the chart ID is assigned to -1
      long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE);
      long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param);
      //--- Get the object, whose properties were changed or which was relocated,
      //--- from the collection list by its name set in sparam
      obj=this.GetStdGraphObject(sparam,chart_id);

      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      if(obj==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj==NULL)
            return;
         //--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         string name_new=this.FindExtraObj(chart_id);
         //--- Send an event with the old name of an object to the control program chart and
         //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),0,obj.Name());
         obj.SetName(name_new);
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj.PropertiesRefresh();
      obj.PropertiesCheckChanged();
     }
//--- Handle standard graphical object events
   if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE)
     {
      //--- Depending on the event type, display an appropriate message in the journal
      switch(idx)
        {
         case GRAPH_OBJ_EVENT_CREATE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_CHANGE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_RENAME   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME));
           obj=this.GetStdGraphObject(sparam,lparam);
           if(obj!=NULL)
              obj.PrintShort();
           break;
         case GRAPH_OBJ_EVENT_DELETE   :
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE));
           break;
         case GRAPH_OBJ_EVENT_DEL_CHART:
           ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": ChartID: ",lparam,", ChartSymbol: ",sparam);
           break;
         default:
           break;
        }
     }
  }
//+------------------------------------------------------------------+

The method logic is described in the code comments. Here I added a response to clicking a chart. In case of a double click, an object changes its selection status leading to an object parameter change event. Thus, we are able to handle object selection/deselection events.
Finally, already handled graphical object events (base events have been created for) arrive to the same method. These base events are handled in the new code block. Currently, we only have sending messages to the journal here. In subsequent articles, I will create a full-fledged handling of each of events so that the program is able to know and quickly access the changed object and its properties.


To simplify handling graphical objects, create the methods for handling graphical objects in the main class of the CEngine library in \MQL5\Include\DoEasy\Engine.mqh.

In order to define open charts while using standard graphical object classes, we can have a look at the list of chart management objects. The list allows us to retrieve IDs of all open charts from objects, thus gaining the ability to access them. To simplify receiving IDs in the program, create the method filling in the long array passed to it with open chart IDs control objects are created for:

//--- Launch the new pause countdown
   void                 Pause(const ulong pause_msc,const datetime time_start=0)
                          {
                           this.PauseSetWaitingMSC(pause_msc);
                           this.PauseSetTimeBegin(time_start*1000);
                           while(!this.PauseIsCompleted() && !::IsStopped()){}
                          }

//--- Return the graphical object collection
   CGraphElementsCollection *GetGraphicObjCollection(void)              { return &this.m_graph_objects; }
   
//--- Fill in the array with IDs of the charts opened in the terminal
   void              GraphGetArrayChartsID(long &array_charts_id[])
                       {
                        CArrayObj *list=this.m_graph_objects.GetListChartsControl();
                        if(list==NULL)
                           return;
                        ::ArrayResize(array_charts_id,list.Total());
                        ::ArrayInitialize(array_charts_id,WRONG_VALUE);
                        for(int i=0;i<list.Total();i++)
                          {
                           CChartObjectsControl *obj=list.At(i);
                           if(obj==NULL)
                              continue;
                           array_charts_id[i]=obj.ChartID();
                          }
                       }

//--- Create the "Vertical line" graphical object

Here we immediately set the size of the passed array equal to the size of the chart management object list and initialize the array with -1 values. This is necessary in case of an error when receiving the chart management object in the loop in order to set -1 in the array cell corresponding to the index of the chart management object, which we failed to get. This serves as an error signal when obtaining the object.
Of course, I can increase the array size in the loop only if an object has been successfully received but calling ArrayResize() inside the loop slows down performance. Therefore, increase the array according to the list size right away. In case of an error, the appropriate array cell will contain -1.
Next, in the loop by the obtained list, get the next chart management object and write the chart ID (contained in the chart management object) to the array cell corresponding to the loop index.

For a simplified access to the methods for creating standard graphical objects, write the methods for their creation — two methods per each object. The first method calls the same-name graphical element collection class for creating a standard graphical object, while the second one calls the first method specifying the current chart ID:

//--- Create the "Vertical line" graphical object
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time)
                       { return this.m_graph_objects.CreateLineVertical(chart_id,name,subwindow,time); }
   bool              CreateLineVertical(const string name,const int subwindow,const datetime time)
                       { return this.CreateLineVertical(::ChartID(),name,subwindow,time); }

//--- Create the "Horizontal line" graphical object
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const double price)
                       { return this.m_graph_objects.CreateLineHorizontal(chart_id,name,subwindow,price); }
   bool              CreateLineHorizontal(const string name,const int subwindow,const double price)
                       { return this.CreateLineHorizontal(::ChartID(),name,subwindow,price); }
 
//--- Create the "Trend line" graphical object
   bool              CreateLineTrend(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateLineTrend(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateLineTrend(const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateLineTrend(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Trend line by angle" graphical object
   bool              CreateLineTrendByAngle(const long chart_id,const string name,const int subwindow,
                                            const datetime time1,const double price1,const datetime time2,const double price2,
                                            const double angle)
                       { return this.m_graph_objects.CreateLineTrendByAngle(chart_id,name,subwindow,time1,price1,time2,price2,angle); }
   bool              CreateLineTrendByAngle(const string name,const int subwindow,
                                            const datetime time1,const double price1,const datetime time2,const double price2,
                                            const double angle)
                       { return this.CreateLineTrendByAngle(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); }

//--- Create the "Cyclic lines" graphical object
   bool              CreateLineCycle(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateLineCycle(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateLineCycle(const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateLineCycle(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Arrowed line" graphical object
   bool              CreateLineArrowed(const long chart_id,const string name,const int subwindow,
                                       const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateLineArrowed(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateLineArrowed(const string name,const int subwindow,
                                       const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateLineArrowed(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Equidistant channel" graphical object
   bool              CreateChannel(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const datetime time3,const double price3)
                       { return this.m_graph_objects.CreateChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreateChannel(const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const datetime time3,const double price3)
                       { return this.CreateChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the "Standard deviation channel" graphical object
   bool              CreateChannelStdDeviation(const long chart_id,const string name,const int subwindow,
                                               const datetime time1,const double price1,const datetime time2,const double price2,
                                               const double deviation=1.5)
                       { return this.m_graph_objects.CreateChannelStdDeviation(chart_id,name,subwindow,time1,price1,time2,price2,deviation); }
   bool              CreateChannelStdDeviation(const string name,const int subwindow,
                                               const datetime time1,const double price1,const datetime time2,const double price2,
                                               const double deviation=1.5)
                       { return this.CreateChannelStdDeviation(::ChartID(),name,subwindow,time1,price1,time2,price2,deviation); }

//--- Create the "Linear regression channel" graphical object
   bool              CreateChannelRegression(const long chart_id,const string name,const int subwindow,
                                             const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateChannelRegression(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateChannelRegression(const string name,const int subwindow,
                                             const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateChannelRegression(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Andrews' Pitchfork" graphical object
   bool              CreatePitchforkAndrews(const long chart_id,const string name,const int subwindow,
                                            const datetime time1,const double price1,const datetime time2,const double price2,
                                            const datetime time3,const double price3)
                       { return this.m_graph_objects.CreatePitchforkAndrews(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreatePitchforkAndrews(const string name,const int subwindow,
                                            const datetime time1,const double price1,const datetime time2,const double price2,
                                            const datetime time3,const double price3)
                       { return this.CreatePitchforkAndrews(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the "Gann line" graphical object
   bool              CreateGannLine(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,const double price2,const double angle)
                       { return this.m_graph_objects.CreateGannLine(chart_id,name,subwindow,time1,price1,time2,price2,angle); }
   bool              CreateGannLine(const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,const double price2,const double angle)
                       { return this.CreateGannLine(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); }

//--- Create the "Gann fan" graphical object
   bool              CreateGannFan(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const ENUM_GANN_DIRECTION direction,const double scale)
                       { return this.m_graph_objects.CreateGannFan(chart_id,name,subwindow,time1,price1,time2,price2,direction,scale); }
   bool              CreateGannFan(const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const ENUM_GANN_DIRECTION direction,const double scale)
                       { return this.CreateGannFan(::ChartID(),name,subwindow,time1,price1,time2,price2,direction,scale); }

//--- Create the "Gann grid" graphical object
   bool              CreateGannGrid(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,
                                    const ENUM_GANN_DIRECTION direction,const double scale)
                       { return this.m_graph_objects.CreateGannGrid(chart_id,name,subwindow,time1,price1,time2,direction,scale); }
   bool              CreateGannGrid(const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,
                                    const ENUM_GANN_DIRECTION direction,const double scale)
                       { return this.CreateGannGrid(::ChartID(),name,subwindow,time1,price1,time2,direction,scale); }

//--- Create the "Fibo levels" graphical object
   bool              CreateFiboLevels(const long chart_id,const string name,const int subwindow,
                                      const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateFiboLevels(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateFiboLevels(const string name,const int subwindow,
                                      const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateFiboLevels(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Fibo Time Zones" graphical object
   bool              CreateFiboTimeZones(const long chart_id,const string name,const int subwindow,
                                         const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateFiboTimeZones(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateFiboTimeZones(const string name,const int subwindow,
                                         const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateFiboTimeZones(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Fibo fan" graphical object
   bool              CreateFiboFan(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateFiboFan(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateFiboFan(const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateFiboFan(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the "Fibo arc" graphical object
   bool              CreateFiboArc(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const double scale,const bool ellipse)
                       { return this.m_graph_objects.CreateFiboArc(chart_id,name,subwindow,time1,price1,time2,price2,scale,ellipse); }
   bool              CreateFiboArc(const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,
                                   const double scale,const bool ellipse)
                       { return this.CreateFiboArc(::ChartID(),name,subwindow,time1,price1,time2,price2,scale,ellipse); }

//--- Create the "Fibo channel" graphical object
   bool              CreateFiboChannel(const long chart_id,const string name,const int subwindow,
                                       const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.m_graph_objects.CreateFiboChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreateFiboChannel(const string name,const int subwindow,
                                       const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.CreateFiboChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the "Fibo extension" graphical object
   bool              CreateFiboExpansion(const long chart_id,const string name,const int subwindow,
                                         const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.m_graph_objects.CreateFiboExpansion(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreateFiboExpansion(const string name,const int subwindow,
                                         const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.CreateFiboExpansion(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the "Elliott 5 waves" graphical object
   bool              CreateElliothWave5(const long chart_id,const string name,const int subwindow,
                                        const datetime time1,const double price1,const datetime time2,const double price2,
                                        const datetime time3,const double price3,const datetime time4,const double price4,
                                        const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree,
                                        const bool draw_lines)
                       { return this.m_graph_objects.CreateElliothWave5(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); }
   bool              CreateElliothWave5(const string name,const int subwindow,
                                        const datetime time1,const double price1,const datetime time2,const double price2,
                                        const datetime time3,const double price3,const datetime time4,const double price4,
                                        const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree,
                                        const bool draw_lines)
                       { return this.CreateElliothWave5(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); }

//--- Create the "Elliott 3 waves" graphical object
   bool              CreateElliothWave3(const long chart_id,const string name,const int subwindow,
                                        const datetime time1,const double price1,const datetime time2,
                                        const double price2,const datetime time3,const double price3,
                                        const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines)
                       { return this.m_graph_objects.CreateElliothWave3(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); }
   bool              CreateElliothWave3(const string name,const int subwindow,
                                        const datetime time1,const double price1,const datetime time2,
                                        const double price2,const datetime time3,const double price3,
                                        const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines)
                       { return this.CreateElliothWave3(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); }

//--- Create the Rectangle graphical object
   bool              CreateRectangle(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.m_graph_objects.CreateRectangle(chart_id,name,subwindow,time1,price1,time2,price2); }
   bool              CreateRectangle(const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       { return this.CreateRectangle(::ChartID(),name,subwindow,time1,price1,time2,price2); }

//--- Create the Triangle graphical object
   bool              CreateTriangle(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.m_graph_objects.CreateTriangle(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreateTriangle(const string name,const int subwindow,
                                    const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.CreateTriangle(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the Ellipse graphical object
   bool              CreateEllipse(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.m_graph_objects.CreateEllipse(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); }
   bool              CreateEllipse(const string name,const int subwindow,
                                   const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3)
                       { return this.CreateEllipse(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); }

//--- Create the "Thumb up" graphical object
   bool              CreateThumbUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateThumbUp(chart_id,name,subwindow,time,price); }
   bool              CreateThumbUp(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateThumbUp(::ChartID(),name,subwindow,time,price); }

//--- Create the "Thumb down" graphical object
   bool              CreateThumbDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateThumbDown(chart_id,name,subwindow,time,price); }
   bool              CreateThumbDown(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateThumbDown(::ChartID(),name,subwindow,time,price); }

//--- Create the "Arrow up" graphical object
   bool              CreateArrowUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateArrowUp(chart_id,name,subwindow,time,price); }
   bool              CreateArrowUp(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateArrowUp(::ChartID(),name,subwindow,time,price); }

//--- Create the "Arrow down" graphical object
   bool              CreateArrowDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateArrowDown(chart_id,name,subwindow,time,price); }
   bool              CreateArrowDown(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateArrowDown(::ChartID(),name,subwindow,time,price); }

//--- Create the Stop graphical object
   bool              CreateSignalStop(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateSignalStop(chart_id,name,subwindow,time,price); }
   bool              CreateSignalStop(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateSignalStop(::ChartID(),name,subwindow,time,price); }

//--- Create the "Check mark" graphical object
   bool              CreateSignalCheck(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateSignalCheck(chart_id,name,subwindow,time,price); }
   bool              CreateSignalCheck(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateSignalCheck(::ChartID(),name,subwindow,time,price); }

//--- Create the "Left price label" graphical object
   bool              CreatePriceLabelLeft(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreatePriceLabelLeft(chart_id,name,subwindow,time,price); }
   bool              CreatePriceLabelLeft(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreatePriceLabelLeft(::ChartID(),name,subwindow,time,price); }

//--- Create the "Right price label" graphical object
   bool              CreatePriceLabelRight(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreatePriceLabelRight(chart_id,name,subwindow,time,price); }
   bool              CreatePriceLabelRight(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreatePriceLabelRight(::ChartID(),name,subwindow,time,price); }

//--- Create the Buy graphical object
   bool              CreateSignalBuy(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateSignalBuy(chart_id,name,subwindow,time,price); }
   bool              CreateSignalBuy(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateSignalBuy(::ChartID(),name,subwindow,time,price); }

//--- Create the Sell graphical object
   bool              CreateSignalSell(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       { return this.m_graph_objects.CreateSignalSell(chart_id,name,subwindow,time,price); }
   bool              CreateSignalSell(const string name,const int subwindow,const datetime time,const double price)
                       { return this.CreateSignalSell(::ChartID(),name,subwindow,time,price); }

//--- Create the Arrow graphical object
   bool              CreateArrow(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                 const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor)
                       { return this.m_graph_objects.CreateArrow(chart_id,name,subwindow,time,price,arrow_code,anchor); }
   bool              CreateArrow(const string name,const int subwindow,const datetime time,const double price,
                                 const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor)
                       { return this.CreateArrow(::ChartID(),name,subwindow,time,price,arrow_code,anchor); }

//--- Create the Text graphical object
   bool              CreateText(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       { return this.m_graph_objects.CreateText(chart_id,name,subwindow,time,price,text,size,anchor_point,angle); }
   bool              CreateText(const string name,const int subwindow,const datetime time,const double price,
                                const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       { return this.CreateText(::ChartID(),name,subwindow,time,price,text,size,anchor_point,angle); }

//--- Create the "Text label" graphical object
   bool              CreateTextLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,
                                     const string text,const int size,const ENUM_BASE_CORNER corner,
                                     const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       { return this.m_graph_objects.CreateTextLabel(chart_id,name,subwindow,x,y,text,size,corner,anchor_point,angle); }
   bool              CreateTextLabel(const string name,const int subwindow,const int x,const int y,
                                     const string text,const int size,const ENUM_BASE_CORNER corner,
                                     const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       { return this.CreateTextLabel(::ChartID(),name,subwindow,x,y,text,size,corner,anchor_point,angle); }

//--- Create the Button graphical object
   bool              CreateButton(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                  const ENUM_BASE_CORNER corner,const int font_size,const bool button_state)
                       { return this.m_graph_objects.CreateButton(chart_id,name,subwindow,x,y,w,h,corner,font_size,button_state); }
   bool              CreateButton(const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                  const ENUM_BASE_CORNER corner,const int font_size,const bool button_state)
                       { return this.CreateButton(::ChartID(),name,subwindow,x,y,w,h,corner,font_size,button_state); }

//--- Create the Chart graphical object
   bool              CreateChart(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                 const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe)
                       { return this.m_graph_objects.CreateChart(chart_id,name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); }
   bool              CreateChart(const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                 const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe)
                       { return this.CreateChart(::ChartID(),name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); }

//--- Create the Bitmap graphical object
   bool              CreateBitmap(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                  const string image1,const string image2,const ENUM_ANCHOR_POINT anchor)
                       { return this.m_graph_objects.CreateBitmap(chart_id,name,subwindow,time,price,image1,image2,anchor); }
   bool              CreateBitmap(const string name,const int subwindow,const datetime time,const double price,
                                  const string image1,const string image2,const ENUM_ANCHOR_POINT anchor)
                       { return this.CreateBitmap(::ChartID(),name,subwindow,time,price,image1,image2,anchor); }

//--- Create the "Bitmap label" graphical object
   bool              CreateBitmapLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                       const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor,
                                       const bool state)
                       { return this.m_graph_objects.CreateBitmapLabel(chart_id,name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); }
   bool              CreateBitmapLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                       const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor,
                                       const bool state)
                       { return this.CreateBitmapLabel(::ChartID(),name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); }

//--- Create the "Input field" graphical object
   bool              CreateEditField(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                     const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly)
                       { return this.m_graph_objects.CreateEditField(chart_id,name,subwindow,x,y,w,h,font_size,corner,align,readonly); }
   bool              CreateEditField(const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                     const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly)
                       { return this.CreateEditField(::ChartID(),name,subwindow,x,y,w,h,font_size,corner,align,readonly); }

//--- Create the "Economic calendar event" graphical object
   bool              CreateCalendarEvent(const long chart_id,const string name,const int subwindow,const datetime time)
                       { return this.m_graph_objects.CreateCalendarEvent(chart_id,name,subwindow,time); }
   bool              CreateCalendarEvent(const string name,const int subwindow,const datetime time)
                       { return this.CreateCalendarEvent(::ChartID(),name,subwindow,time); }

//--- Create the "Rectangular label" graphical object
   bool              CreateRectangleLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                          const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border)
                       { return this.m_graph_objects.CreateRectangleLabel(chart_id,name,subwindow,x,y,w,h,corner,border); }
   bool              CreateRectangleLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                          const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border)
                       { return this.CreateRectangleLabel(::ChartID(),name,subwindow,x,y,w,h,corner,border); }
   
//--- Constructor/destructor
                        CEngine();
                       ~CEngine();

Currently, these are all the improvements necessary for creating the basic functionality aimed at tracking graphical object events.


Test

To perform the test, I will use the EA from the previous article saving it in \MQL5\Experts\TestDoEasy\Part90\ as TestDoEasyPart90.mq5.

In the previous article, we created a vertical line when clicking on a chart while holding Ctrl. Now we will create it on all open charts in the terminal.

In the chart click handling code block of the OnChartEvent() handler, add the code for filling in the array with IDs of all open charts. In the loop by the obtained array, create a vertical line on each of the charts:

   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
        {
         long array[];
         engine.GraphGetArrayChartsID(array);
         for(int i=0;i<ArraySize(array);i++)
            engine.CreateLineVertical(array[i],"LineVertical",0,time);
        }
     }
   engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam);
   
  }
//+------------------------------------------------------------------+

Compile the EA and launch it on the chart after opening another chart and placing both charts horizontally. Clicking on a chart with an EA creates vertical lines — one line per each chart. Now change their properties and see how the messages about obtained events are displayed in the journal:


As we can see, messages about object events are displayed in the journal. When creating objects programmatically, the object creation event is not created since a programmer already knows at what point in time a graphical object is created. Therefore, there is no need to duplicate the fact by sending the event.
Of course, a simple display of generalized messages in the journal is insufficient for handling events. But these are only the messages about base events whose parameters contain all data about an event defined later.


What's next?

In the next article, I will continue my work on graphical object events and implement handling each obtained event.

All files of the current library version, test EA and chart event control indicator for MQL5 are attached below for you to test and download. Leave your questions, comments and suggestions in the comments.

Back to contents

*Previous articles within the series:

Graphics in DoEasy library (Part 86): Graphical object collection - managing property modification
Graphics in DoEasy library (Part 87): Graphical object collection - managing object property modification on all open charts
Graphics in DoEasy library (Part 88): Graphical object collection — two-dimensional dynamic array for storing dynamically changing object properties
Graphics in DoEasy library (Part 89): Programming standard graphical objects. Basic functionality

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/10139

Attached files |
MQL5.zip (4178.92 KB)
Combinatorics and probability for trading (Part V): Curve analysis Combinatorics and probability for trading (Part V): Curve analysis
In this article, I decided to conduct a study related to the possibility of reducing multiple states to double-state systems. The main purpose of the article is to analyze and to come to useful conclusions that may help in the further development of scalable trading algorithms based on the probability theory. Of course, this topic involves mathematics. However, given the experience of previous articles, I see that generalized information is more useful than details.
Learn how to design different Moving Average systems Learn how to design different Moving Average systems
There are many strategies that can be used to filter generated signals based on any strategy, even by using the moving average itself which is the subject of this article. So, the objective of this article is to share with you some of Moving Average Strategies and how to design an algorithmic trading system.
Matrices and vectors in MQL5 Matrices and vectors in MQL5
By using special data types 'matrix' and 'vector', it is possible to create code which is very close to mathematical notation. With these methods, you can avoid the need to create nested loops or to mind correct indexing of arrays in calculations. Therefore, the use of matrix and vector methods increases the reliability and speed in developing complex programs.
Combinatorics and probability for trading (Part IV): Bernoulli Logic Combinatorics and probability for trading (Part IV): Bernoulli Logic
In this article, I decided to highlight the well-known Bernoulli scheme and to show how it can be used to describe trading-related data arrays. All this will then be used to create a self-adapting trading system. We will also look for a more generic algorithm, a special case of which is the Bernoulli formula, and will find an application for it.