MQL5 Cookbook: Position Properties on the Custom Info Panel

Anatoli Kazharski | 14 March, 2013

Introduction

This time we will create a simple Expert Advisor that will get position properties on the current symbol and display them on the custom info panel during manual trading. The info panel will be created using graphical objects and displayed information will be refreshed at every tick. This is going to be much more convenient than all the time having to manually run the script described in the previous article of the series called "MQL5 Cookbook: Getting Position Properties".

 

Developing an Expert Advisor

Let us start with graphical objects. To create the info panel, we need objects for the background, header, names and values of position properties. The background and header will require a rectangle that does not move with the price. The rectangle can be created using such graphical objects as Rectangle Label or Edit, while the names and values of object properties will be made using Text Labels.

Before we proceed with the code, we will first prepare a layout for the info panel. Its convenience lies in the fact that we can quickly change any property in the settings window and customize the look of the info panel.

Every object has a settings window that can be opened from the context menu of a selected object. The settings window can also be opened from the Object List (Ctrl+B) by selecting the required object and clicking on Properties. The info panel layout is shown below. It can also be used to estimate sizes and coordinates when writing a code. When the code for the info panel is ready, you will have to delete the layout objects manually as the Expert Advisor will not be able to 'see' them and will therefore not remove them from the chart.

Fig. 1. Preparing the layout for the info panel.

Fig. 1. Preparing the layout for the info panel.

Now we need to create a template for the Expert Advisor. This can be done as quickly as for the script. In MQL5 Wizard, the Expert Advisor (template) option is selected by default. We go through the next steps without making any changes to the options as they are not needed this time. Then click Finish and you will see a template as below:

//+------------------------------------------------------------------+
//|                                      PositionPropertiesPanel.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

One can notice right away that the Expert Advisor template is different from the script template. Apart from the program properties (#property), there are three main functions: OnInit(), OnDeinit() and OnTick().

The OnInit() function is called when loading the program, changing external parameters, compiling the program, provided that the program is at that time added to the chart, and when changing the symbol or period. If necessary, you can initialize certain variables or arrays in this function to be able to work with them later on.

The OnDeinit() function is called when you delete the program from the chart, change the account, symbol or period. All possible deinitialization reasons are provided in MQL5 Reference. This Expert Advisor will employ a user-defined function, GetDeinitReasonText(), that converts the deinitialization reason identifier (the OnDeinit() function parameter) to text.

And finally, the OnTick() function. It is called every time there is a new tick on the symbol on whose chart the Expert Advisor is currently operating.

Let us now prepare all constants, variables and arrays that we are going to use in the Expert Advisor. We will place them at the very beginning of the program. First, define the variables whose values remain unchanged throughout the program:

//---
#define INFOPANEL_SIZE 14 // Size of the array for info panel objects
#define EXPERT_NAME MQL5InfoString(MQL5_PROGRAM_NAME) // Name of the Expert Advisor
//---

This is followed by global variables for position properties:

//--- GLOBAL VARIABLES
bool                 pos_open=false;         // Flag of presence/absence of an open position
string               pos_symbol="";          // Symbol
long                 pos_magic=0;            // Magic number
string               pos_comment="";         // Comment
double               pos_swap=0.0;           // Swap
double               pos_commission=0.0;     // Commission
double               pos_price=0.0;          // Position price
double               pos_cprice=0.0;         // Current price of the position
double               pos_profit=0.0;         // Profit/Loss of the position
double               pos_volume=0.0;         // Position volume
double               pos_sl=0.0;             // Stop Loss of the position
double               pos_tp=0.0;             // Take Profit of the position
datetime             pos_time=NULL;          // Position opening time
long                 pos_id=0;               // Position identifier
ENUM_POSITION_TYPE   pos_type=WRONG_VALUE;   // Position type

After the variables, we will declare arrays of graphical object names. These objects will display position properties and their values in the chart. For this purpose, we will create two string arrays and immediately initialize their elements to values. In square brackets, we use the value of the INFOPANEL_SIZE constant declared at the very beginning of the program. That is, there will be 14 elements in each array.

// Array of names of objects that display names of position properties
string positionPropertyNames[INFOPANEL_SIZE]=
  {
   "name_pos_symbol",
   "name_pos_magic",
   "name_pos_comment",
   "name_pos_swap",
   "name_pos_commission",
   "name_pos_price",
   "name_pos_cprice",
   "name_pos_profit",
   "name_pos_volume",
   "name_pos_sl",
   "name_pos_tp",
   "name_pos_time",
   "name_pos_id",
   "name_pos_type"
  };
//---
// Array of names of objects that display values of position properties
string positionPropertyValues[INFOPANEL_SIZE]=
  {
   "value_pos_symbol",
   "value_pos_magic",
   "value_pos_comment",
   "value_pos_swap",
   "value_pos_commission",
   "value_pos_price",
   "value_pos_cprice",
   "value_pos_profit",
   "value_pos_volume",
   "value_pos_sl",
   "value_pos_tp",
   "value_pos_time",
   "value_pos_id",
   "value_pos_type"
  };
//---

Using these names, you can programmatically find the necessary object in the chart and set or change its properties such as displayed text, color, size, etc. Besides, these names are going to be displayed in the Object List (Ctrl+B) window after being created in the chart. But you will not be able to see them there as the objects created by the MQL5 program are hidden by default. To make them visible, you should click on List All in the Object List window. This feature helps to separate the objects created manually from the ones created programmatically which is admittedly very convenient.

Further, we will need user-defined functions which will be employed by the Expert Advisor to create graphical objects. The function offered by MQL5 for creation of graphical objects is ObjectCreate(). But since we also need to set object properties, while the objects themselves might need to be created more than once, it would be better to think of a more convenient and compact method that could be implemented in a single line of code.

To create the info panel background and header, we are going to use the graphical object Edit. Let us write the CreateEdit() function:

//+------------------------------------------------------------------+
//| CREATING THE EDIT OBJECT                                         |
//+------------------------------------------------------------------+
void CreateEdit(long             chart_id,         // chart id
                int              sub_window,       // (sub)window number
                string           name,             // object name
                string           text,             // displayed text
                ENUM_BASE_CORNER corner,           // chart corner
                string           font_name,        // font
                int              font_size,        // font size
                color            font_color,       // font color
                int              x_size,           // width
                int              y_size,           // height
                int              x_distance,       // X-coordinate
                int              y_distance,       // Y-coordinate
                long             z_order,          // Z-order
                color            background_color, // background color
                bool             read_only)        // Read Only flag
  {
// If the object has been created successfully,...
   if(ObjectCreate(chart_id,name,OBJ_EDIT,sub_window,0,0))
     {
      // ...set its properties
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);                 // displayed text
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);            // set the chart corner
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);            // set the font
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);       // set the font size
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);         // font color
      ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color); // background color
      ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size);             // width
      ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size);             // height
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);     // set the X coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);     // set the Y coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);         // cannot select the object if FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);           // Z-order of the object
      ObjectSetInteger(chart_id,name,OBJPROP_READONLY,read_only);       // Read Only
      ObjectSetInteger(chart_id,name,OBJPROP_ALIGN,ALIGN_LEFT);         // align left
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");              // no tooltip if "\n"
     }
  }

Now the graphical object Edit (OBJ_EDIT) can be created using a single line of code. We will illustrate it with an example when creating a function that will set the info panel on the chart.

Now let us continue to the Text Label objects which are going to be used to display the list of position properties and their values, and create the CreateLabel() function in a similar way:

//+------------------------------------------------------------------+
//| CREATING THE LABEL OBJECT                                        |
//+------------------------------------------------------------------+
void CreateLabel(long               chart_id,   // chart id
                 int                sub_window, // (sub)window number
                 string             name,       // object name
                 string             text,       // displayed text
                 ENUM_ANCHOR_POINT  anchor,     // anchor point
                 ENUM_BASE_CORNER   corner,     // chart corner
                 string             font_name,  // font
                 int                font_size,  // font size
                 color              font_color, // font color
                 int                x_distance, // X-coordinate
                 int                y_distance, // Y-coordinate
                 long               z_order)    // Z-order
  {
// If the object has been created successfully,...
   if(ObjectCreate(chart_id,name,OBJ_LABEL,sub_window,0,0))
     {
      // ...set its properties
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);              // displayed text
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);         // set the font
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);      // set the font color
      ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor);         // set the anchor point
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);         // set the chart corner
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);    // set the font size
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);  // set the X-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);  // set the Y-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);      // cannot select the object if FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);        // Z-order of the object
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");           // no tooltip if "\n"
     }
  }

It is also advisable to have a look at function descriptions in MQL5 Reference.

When being deleted from the chart, the Expert Advisor must in turn delete all objects that it previously added to the chart. To do this, you can simply pass the object name to the DeleteObjectByName() function. It will then search for the object by the name specified and delete it, if found, using the built-in ObjectFind() function that searches for the object and the ObjectDelete() function that deletes the object.

//+------------------------------------------------------------------+
//| DELETING THE OBJECT BY NAME                                      |
//+------------------------------------------------------------------+
void DeleteObjectByname(string name)
  {
   int  sub_window=0;      // Returns the number of the subwindow where the object is located
   bool res       =false;  // Result following an attempt to delete the object
//--- Find the object by name
   sub_window=ObjectFind(ChartID(),name);
//---
   if(sub_window>=0) // If it has been found,..
     {
      res=ObjectDelete(ChartID(),name); // ...delete it
      //---
      // If an error occurred when deleting the object,..
      if(!res) // ...print the relevant message
        {
         Print("Error deleting the object: ("+IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError()));
        }
     }
  }

Further, in the DeleteObjectByName() function, we also implement a check for errors when deleting an object. If an error occurs, a relevant message will appear containing the error code and description. As you can see in the code above, we use an additional user-defined function that converts the error code to textual description - the ErrorDescription() function. Since there are lots of error codes, I will exemplify the above using only a part of this function (see the code below). The full version of the code can be found in the source code file attached to this article.

//+------------------------------------------------------------------+
//| RETURNING THE ERROR DESCRIPTION                                  |
//+------------------------------------------------------------------+
string ErrorDescription(int error_code)
  {
   string error_string="";
//---
   switch(error_code)
     {
      //--- Trade server return codes

      case 10004: error_string="Requote";                                                                break;
      case 10006: error_string="Request rejected";                                                       break;
      case 10007: error_string="Request canceled by trader";                                             break;
      case 10008: error_string="Order placed";                                                           break;
      case 10009: error_string="Request executed";                                                       break;
      case 10010: error_string="Request executed partially";                                             break;
      case 10011: error_string="Request processing error";                                               break;
      case 10012: error_string="Request timed out";                                                      break;
      case 10013: error_string="Invalid request";                                                        break;
      case 10014: error_string="Invalid request volume";                                                 break;
      case 10015: error_string="Invalid request price";                                                  break;
      case 10016: error_string="Invalid Stop orders in the request";                                     break;
      case 10017: error_string="Trading forbidden";                                                      break;
      case 10018: error_string="Market is closed";                                                       break;
      case 10019: error_string="Insufficient funds";                                                     break;
      case 10020: error_string="Prices changed";                                                         break;
      case 10021: error_string="No quotes to process the request";                                       break;
      case 10022: error_string="Invalid order expiration in the request";                                break;
      case 10023: error_string="Order status changed";                                                   break;
      case 10024: error_string="Too many requests";                                                      break;
      case 10025: error_string="No changes in the request";                                              break;
      case 10026: error_string="Automated trading is disabled by trader";                                break;
      case 10027: error_string="Automated trading is disabled by the client terminal";                   break;
      case 10028: error_string="Request blocked for processing";                                         break;
      case 10029: error_string="Order or position frozen";                                               break;
      case 10030: error_string="The specified type of order execution by balance is not supported";      break;
      case 10031: error_string="No connection with trade server";                                        break;
      case 10032: error_string="Transaction is allowed for live accounts only";                          break;
      case 10033: error_string="You have reached the maximum number of pending orders";                  break;
      case 10034: error_string="You have reached the maximum order and position volume for this symbol"; break;

      ...

     }
//---
   return(error_string);
  }

In the previous article we dealt with the GetPositionProperties() function that gets position properties. This time the function structure is going to be a bit more complex. We will check for a position that is currently open, with the flag of presence/absence of an open position being stored in the global variable pos_open. This information may be required in other functions, without having to call the PositionSelect() function every time.

Then, if an open position exists, we will get its properties, otherwise all variables will be zeroed out. Now let us write a simple ZeroPositionProperties() function:

//+------------------------------------------------------------------+
//| ZEROING OUT VARIABLES FOR POSITION PROPERTIES                    |
//+------------------------------------------------------------------+
void ZeroPositionProperties()
  {
   pos_symbol     ="";
   pos_comment    ="";
   pos_magic      =0;
   pos_price      =0.0;
   pos_cprice     =0.0;
   pos_sl         =0.0;
   pos_tp         =0.0;
   pos_type       =WRONG_VALUE;
   pos_volume     =0.0;
   pos_commission =0.0;
   pos_swap       =0.0;
   pos_profit     =0.0;
   pos_time       =NULL;
   pos_id         =0;
  }

Further, at the end of the GetPositionProperties() function, we will call a user-defined SetInfoPanel() function that draws/updates the info panel in the chart.

//+------------------------------------------------------------------+
//| GETTING POSITION PROPERTIES                                      |
//+------------------------------------------------------------------+
void GetPositionProperties()
  {
// Check if there is an open position
   pos_open=PositionSelect(_Symbol);
//---
   if(pos_open) // If an open position exists, get its properties
     {
      pos_symbol     =PositionGetString(POSITION_SYMBOL);
      pos_comment    =PositionGetString(POSITION_COMMENT);
      pos_magic      =PositionGetInteger(POSITION_MAGIC);
      pos_price      =PositionGetDouble(POSITION_PRICE_OPEN);
      pos_cprice     =PositionGetDouble(POSITION_PRICE_CURRENT);
      pos_sl         =PositionGetDouble(POSITION_SL);
      pos_tp         =PositionGetDouble(POSITION_TP);
      pos_type       =(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      pos_volume     =PositionGetDouble(POSITION_VOLUME);
      pos_commission =PositionGetDouble(POSITION_COMMISSION);
      pos_swap       =PositionGetDouble(POSITION_SWAP);
      pos_profit     =PositionGetDouble(POSITION_PROFIT);
      pos_time       =(datetime)PositionGetInteger(POSITION_TIME);
      pos_id         =PositionGetInteger(POSITION_IDENTIFIER);
     }
   else // If there is no open position, zero out variables for position properties
      ZeroPositionProperties();
//---
   SetInfoPanel(); // Set/update the info panel
  }

Let us now write the SetInfoPanel() function. Below is the code of the function with detailed comments:

//+------------------------------------------------------------------+
//| SETTING THE INFO PANEL                                           |
//|------------------------------------------------------------------+
void SetInfoPanel()
  {
   int               y_bg=18;             // Y-coordinate for the background and header
   int               y_property=32;       // Y-coordinate for the list of properties and their values
   int               line_height=12;      // Line height
//---
   int               font_size=8;         // Font size
   string            font_name="Calibri"; // Font
   color             font_color=clrWhite; // Font color
//---
   ENUM_ANCHOR_POINT anchor=ANCHOR_RIGHT_UPPER; // Anchor point in the top right corner
   ENUM_BASE_CORNER  corner=CORNER_RIGHT_UPPER; // Origin of coordinates in the top right corner of the chart
//--- X-coordinates
   int               x_first_column=120// First column (names of properties)
   int               x_second_column=10;  // Second column (values of properties)
//--- Array of Y-coordinates for the names of position properties and their values
   int               y_prop_array[INFOPANEL_SIZE]={0};
//--- Fill the array with coordinates for each line on the info panel
   y_prop_array[0]=y_property;
   y_prop_array[1]=y_property+line_height;
   y_prop_array[2]=y_property+line_height*2;
   y_prop_array[3]=y_property+line_height*3;
   y_prop_array[4]=y_property+line_height*4;
   y_prop_array[5]=y_property+line_height*5;
   y_prop_array[6]=y_property+line_height*6;
   y_prop_array[7]=y_property+line_height*7;
   y_prop_array[8]=y_property+line_height*8;
   y_prop_array[9]=y_property+line_height*9;
   y_prop_array[10]=y_property+line_height*10;
   y_prop_array[11]=y_property+line_height*11;
   y_prop_array[12]=y_property+line_height*12;
   y_prop_array[13]=y_property+line_height*13;
//--- Background of the info panel
   CreateEdit(0,0,"InfoPanelBackground","",corner,font_name,8,clrWhite,230,190,231,y_bg,0,C'15,15,15',true);
//--- Header of the info panel
   CreateEdit(0,0,"InfoPanelHeader","POSITION PROPERTIES",corner,font_name,8,clrWhite,230,14,231,y_bg,1,clrFireBrick,true);
//--- List of the names of position properties and their values
//    Property name
   CreateLabel(0,0,pos_prop_names[0],"Symbol :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[0],2);
//    Property value
   CreateLabel(0,0,pos_prop_values[0],GetValInfoPanel(0),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[0],2);
//---
   CreateLabel(0,0,pos_prop_names[1],"Magic Number :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[1],2);
   CreateLabel(0,0,pos_prop_values[1],GetValInfoPanel(1),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[1],2);
//---
   CreateLabel(0,0,pos_prop_names[2],"Comment :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[2],2);
   CreateLabel(0,0,pos_prop_values[2],GetValInfoPanel(2),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[2],2);
//---
   CreateLabel(0,0,pos_prop_names[3],"Swap :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[3],2);
   CreateLabel(0,0,pos_prop_values[3],GetValInfoPanel(3),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[3],2);
//---
   CreateLabel(0,0,pos_prop_names[4],"Commission :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[4],2);
   CreateLabel(0,0,pos_prop_values[4],GetValInfoPanel(4),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[4],2);
//---
   CreateLabel(0,0,pos_prop_names[5],"Open Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[5],2);
   CreateLabel(0,0,pos_prop_values[5],GetValInfoPanel(5),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[5],2);
//---
   CreateLabel(0,0,pos_prop_names[6],"Current Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[6],2);
   CreateLabel(0,0,pos_prop_values[6],GetValInfoPanel(6),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[6],2);
//---
   CreateLabel(0,0,pos_prop_names[7],"Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[7],2);
   CreateLabel(0,0,pos_prop_values[7],GetValInfoPanel(7),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[7],2);
//---
   CreateLabel(0,0,pos_prop_names[8],"Volume :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[8],2);
   CreateLabel(0,0,pos_prop_values[8],GetValInfoPanel(8),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[8],2);
//---
   CreateLabel(0,0,pos_prop_names[9],"Stop Loss :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[9],2);
   CreateLabel(0,0,pos_prop_values[9],GetValInfoPanel(9),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[9],2);
//---
   CreateLabel(0,0,pos_prop_names[10],"Take Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[10],2);
   CreateLabel(0,0,pos_prop_values[10],GetValInfoPanel(10),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[10],2);
//---
   CreateLabel(0,0,pos_prop_names[11],"Time :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[11],2);
   CreateLabel(0,0,pos_prop_values[11],GetValInfoPanel(11),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[11],2);
//---
   CreateLabel(0,0,pos_prop_names[12],"Identifier :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[12],2);
   CreateLabel(0,0,pos_prop_values[12],GetValInfoPanel(12),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[12],2);
//---
   CreateLabel(0,0,pos_prop_names[13],"Type :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[13],2);
   CreateLabel(0,0,pos_prop_values[13],GetValInfoPanel(13),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[13],2);
//---
   ChartRedraw(); // Redraw the chart
  }

Let us take a closer look at the SetInfoPanel() function. Variables that have to do with properties of the graphical objects (coordinates, color, font, displayed text, etc.) are declared at the beginning of the function. Pay attention to the process of filling the array of Y-coordinates for the list of position properties on the info panel. It is implemented in a way that is clear to beginners. But it can be reduced to a couple of lines of code when using a loop. You can write it as follows:

//--- Fill the array with coordinates for each line on the info panel
   for(int i=0; i<INFOPANEL_SIZE; i++)
     {
      if(i==0) y_prop_array[i]=y_property;
      else     y_prop_array[i]=y_property+line_height*i;
     }

Then, all properties of the objects that need to be displayed on the panel must be specified in the parameters of the earlier created CreateLabel() and CreateEdit() functions, taking one object at a time. The entire list can also be implemented in a few lines of code using a loop. To do this, we need to create another array for objects that display the text of the names of position properties in the chart. Let this be your homework.

The GetPropertyValue() function that receives the object number returns the value which is then passed to the CreateLabel() function as the fourth parameter (displayed text). This concerns all objects that will display values of position properties. The value returned by the function is an adjusted string value which will ultimately be displayed on the panel. Below is the code of the function with detailed comments:

//+------------------------------------------------------------------+
//| RETURNING THE STRING WITH POSITION PROPERTY VALUE                |
//+------------------------------------------------------------------+
string GetPropertyValue(int number)
  {
//--- Sign indicating the lack of an open position or a certain property
//    E.g. the lack of a comment, Stop Loss or Take Profit
   string empty="-";
//--- If an open position exists, return the value of the requested property
   if(pos_open)
     {
      switch(number)
        {
         case 0  : return(pos_symbol);                                           break;
         case 1  : return(IntegerToString((int)pos_magic));                      break;
         //--- return the value of the comment, if any, otherwise return the sign indicating the lack of comment
         case 2  : return(pos_comment!="" ? pos_comment : empty);                break;
         case 3  : return(DoubleToString(pos_swap,2));                           break;
         case 4  : return(DoubleToString(pos_commission,2));                     break;
         case 5  : return(DoubleToString(pos_price,_Digits));                    break;
         case 6  : return(DoubleToString(pos_cprice,_Digits));                   break;
         case 7  : return(DoubleToString(pos_profit,2));                         break;
         case 8  : return(DoubleToString(pos_volume,2));                         break;
         case 9  : return(pos_sl!=0.0 ? DoubleToString(pos_sl,_Digits) : empty); break;
         case 10 : return(pos_tp!=0.0 ? DoubleToString(pos_tp,_Digits) : empty); break;
         case 11 : return(TimeToString(pos_time,TIME_DATE|TIME_MINUTES));        break;
         case 12 : return(IntegerToString((int)pos_id));                         break;
         case 13 : return(PositionTypeToString(pos_type));                       break;
         
         default : return(empty);
        }
     }
//---
// If there is no open position, return the sign indicating the lack of the open position "-"
   return(empty);
  }

The code above suggests that a certain value is prepared for every number passed to the function, provided that there is an open position. If there is currently no open position, the function will return a dash (-) displayed for all objects that have to do with position property values.

At the end of the SetInfoPanel() function, we call the ChartRedraw() function designed for a forced chart redraw. Unless it is called, you will not be able to see the changes made.

Now we need to write a function that will delete all graphical objects created by the Expert Advisor. Let us call it DeleteInfoPanel():

//+------------------------------------------------------------------+
//| DELETING THE INFO PANEL                                          |
//+------------------------------------------------------------------+
void DeleteInfoPanel()
  {
   DeleteObjectByName("InfoPanelBackground");   // Delete the panel background
   DeleteObjectByName("InfoPanelHeader");       // Delete the panel header
//--- Delete position properties and their values
   for(int i=0; i<INFOPANEL_SIZE; i++)
     {
      DeleteObjectByName(pos_prop_names[i]);    // Delete the property
      DeleteObjectByName(pos_prop_values[i]);   // Delete the value
     }
//---
   ChartRedraw(); // Redraw the chart
  }

Now we only need to distribute the methods we created among the main functions of the Expert Advisor that were originally present in the template after creating it in MQL5 Wizard. This is the easiest part:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get the properties and set the panel
   GetPositionProperties();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Print the deinitialization reason to the journal
   Print(GetDeinitReasonText(reason));
//--- When deleting from the chart
   if(reason==REASON_REMOVE)
      //--- Delete all objects relating to the info panel from the chart
      DeleteInfoPanel();

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Get the properties and update the values on the panel
   GetPositionProperties();

  }
//+------------------------------------------------------------------+

The only thing you might stumble upon is the GetDeinitReasonText() function which returns a textual description of the deinitialization reason code:

//+---------------------------------------------------------------------+
//| RETURNING A TEXTUAL DESCRIPTION OF THE DEINITIALIZATION REASON CODE |
//+---------------------------------------------------------------------+
string GetDeinitReasonText(int reason_code)
  {
   string text="";
//---
   switch(reason_code)
     {
      case REASON_PROGRAM :     // 0
         text="The Expert Advisor has stopped working calling the ExpertRemove() function.";   break;
      case REASON_REMOVE :      // 1
         text="The '"+EXPERT_NAME+"' program has been removed from the chart.";                break;
      case REASON_RECOMPILE :   // 2
         text="The '"+EXPERT_NAME+"' program has been recompiled.";                            break;
      case REASON_CHARTCHANGE : // 3
         text="Chart symbol or period has been changed.";                                      break;
      case REASON_CHARTCLOSE :  // 4
         text="The chart is closed.";                                                          break;
      case REASON_PARAMETERS :  // 5
         text="Input parameters have been changed by the user.";                               break;
      case REASON_ACCOUNT :     // 6
         text="A different account has been activated.";                                       break;
      case REASON_TEMPLATE :    // 7
         text="A different chart template has been applied.";                                  break;
      case REASON_INITFAILED :  // 8
         text="A flag specifying that the OnInit() handler returned zero value.";              break;
      case REASON_CLOSE :       // 9
         text="The terminal has been closed.";                                                 break;
      default : text="The reason is undefined.";
     }
//---
   return text;
  }

If you try to use the Expert Advisor on the chart symbol that currently has no open position, you will see dashes instead of position property values on the panel. The panel will look the same after you close a certain position.

Fig. 2. Info panel in the absence of an open position.

Fig. 2. Info panel in the absence of an open position.

If the Expert Advisor is added to the chart of the symbol that has an open position or if a position is opened after adding the Expert Advisor to the chart, all dashes will be replaced with the appropriate position property values:

Fig. 3. Info panel displaying properties of the open position.

Fig. 3. Info panel displaying properties of the open position.

There is one little peculiarity. After closing the position, it is not until the new tick that the values on the panel are updated. There is a way to make the values be updated immediately but what needs to be done to implement this will be discussed in the next article of the series.

 

Conclusion

Some of the functions introduced in this article will also be used in the following articles of the MQL5 Cookbook series, while others will be modified and improved depending on the task at hand. It is advisable to read the articles in order, one after another as each new article is a logical continuation of the previous one. It certainly also depends on your level of competence and skills so it might be more reasonable and interesting to start with more recent publications. 

The source code file is attached to the article.