MQL5 Cookbook: Position-Eigenschaften auf dem Angepassten Info-Panel

Anatoli Kazharski | 27 Juni, 2016

Einleitung

Diesmal erzeugen wir einen einfachen Expert Advisor, der die Position-Eigenschaften auf dem aktuellen Symbol abruft und sie im angepassten Info-Panel während manuell durchgeführtem Handel anzeigt. Das Info-Panel wird mit Hilfe graphischer Objekte erstellt, und die angezeigte Information wird bei jeder Kursschwankung (Tick) aktualisiert. Das ist weitaus bequemer als ständig das im vorangegangenen Beitrag der Reihe "MQL5 Cookbook: Wie man Position-Eigenschaften abruft", beschriebene Script manuell laufen lassen zu müssen.

 

Den Expert Advisor entwickeln

Beginnen wir mit den grafischen Objekten. Zum Erstellen des Info-Panels, brauchen wir Objekte für den Hintergrund, die Kopfzeile sowie Namen und Werte der Position-Eigenschaften. Der Hintergrund und die Kopfzeile brauchen ein Rechteck, das sich nicht zusammen mit dem Kurs bewegt. Das Rechteck kann mit Hilfe solcher grafischen Objekte wie Rechteck-Kennung oder Bearbeiten erzeugt werden; die Namen und Werte der Objekteigenschaften macht man mit Hilfe von Text-Kennungen.

Bevor wir mit dem Code weitermachen, legen wir zunächst das Layout für das Info-Panel an. Sein Vorteil liegt in der Tatsache, dass wir im Einstellungsfenster rasch jede Eigenschaft ändern und das Aussehen des Info-Panels individuell anpassen können.

Jedes Objekt hat ein Einstellungsfenster, das vom Kontextmenü eines gewählten Objekts aus aufgerufen werden kann. Das Einstellungsfenster kann auch von der 'Objekt-Liste' aus geöffnet werden (Strg+B), indem man das benötigte Objekt auswählt und 'Eigenschaften' anklickt. Unten sehen Sie das Layout des Info-Panels. Es kann auch zur Abschätzung der Größen und Koordinaten beim Schreiben eines Codes verwendet werden. Ist der Code für das Info-Panel fertig, müssen Sie die Layout-Objekte manuell löschen, da der Expert Advisor sie nicht 'sehen' kann und sie dehalb nicht aus dem Chart entfernt.

Abb. 1 Vorbereitung des Layout für das Info-Panel.

Abb. 1 Vorbereitung des Layout für das Info-Panel.

Jetzt müssen wir ein Template für den Expert Advisor anlegen. Das geht genauso schnell wie für das Script. Die Expert Advisor (Template) Option ist im MQL5-Assistenten standardmäßig ausgewählt. Wir machen die nächsten Schritte ohne die Optionen zu ändern, da wir sie derzeit noch nicht brauchen. Danach auf 'Beenden' klicken und das Template sollte, so wie unten gezeigt, erscheinen:

//+------------------------------------------------------------------+
//|                                      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()
  {
//---
   
  }
//+------------------------------------------------------------------+

Sie sehen sofort, dass sich das Expert Advisor Template vom Script Template unterscheidet. Abgesehen von den Programmeigenschaften (#property), gibt es dort drei Hauptfunktionen: OnInit(), OnDeinit() und OnTick().

Die OnInit() Funktion wird aufgerufen, wenn das Programm geladen wird, externe Parameter verändert werden und das Programm erstellt wird, vorausgesetzt, das Programm ist in diesem Moment dem Chart hinzugefügt sowie wenn das Symbol oder der Zeitraum verändert werden. Sie können in dieser Funktion ggf. bestimmte Variablen oder Arrays initialisieren, um mit ihnen später arbeiten zu können.

Die OnDeinit() Funktion wird aufgerufen, wenn Sie das Programm vom Chart löschen und das Konto, Symbol oder den Zeitraum wechseln. Alle Gründe für eine mögliche De-Initialisierung stehen in den MQL5 Referenzhinweisen. Dieser Expert Advisor setzt eine benutzerdefinierte Funktion ein, GetDeinitReasonText(), die den Identifikator für den Grund der De-Initialisierung (den OnDeinit() Funktionsparameter) in Text umwandelt.

Und schließlich gibt's da noch die OnTick() Funktion. Sie wird jedes Mal dann aufgerufen, wenn auf dem Symbol auf dessen Chart der Expert Advisor derzeit arbeitet, eine neue Kursschwankung auftritt.

Bereiten wir nun alle Konstanten, Variablen und Arrays vor, die wir im Expert Advisor verwenden werden. Sie alle platzieren wir ganz zu Anfang des Programms. Zunächst müssen wir die Variablen festlegen, deren Werte im gesamten Programm unverändert bleiben:

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

Danach kommen die globalen Variablen für die Position-Eigenschaften dran:

//--- 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

Nach den Variablen, deklarieren wir Arrays mit Namen grafischer Objeke. Diese Objekte zeigen später die Position-Eigenschaften und ihre Werte im Chart Zu diesem Zweck erzeugen wir zwei String-Arrays und initialisieren unmittelbar ihre Elemente zu Werten. In eckigen Klammern verwenden wir den Wert der INFOPANEL_SIZE Konstante, die ganz zu Anfang des Programms deklariert wurde. Es gibt also 14 Elemente in jedem 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"
  };
//---

Mit Hilfe dieser Namen können Sie programmatisch das nötige Objekt im Chart finden und seine Eigenschaften einrichten oder verändern, wie z.B. angezeigter Text, Farbe, Größe, usw.Darüber hinaus werden diese Namen in dem Fenster 'Objekt-Liste' (Strg+B) angezeigt, nachdem sie im Chart erzeugt worden sind. Doch Sie können sie dort nicht sehen, da die vom MQL5 Programm erzeugten Objekte standardmäßig verborgen sind Um sie sichtbar zu machen, müssen Sie im Fenster 'Objekt-Liste' auf 'Alle auflisten' klicken. Mit diesem Feature können Sie die manuell erzeugte Objekte von den programmatisch erzeugten abtrennen und unterscheiden - was zugegebenermaßen extrem bequem ist.

Des Weiteren brauchen wir noch benutzerdefinierte Funktionen, die vom Expert Advisor zur Erzeugung grafischer Objekte verwendet werden. Die zur Erzeugung von grafischen Objekten von MQL5 angebotene Funktion ist ObjectCreate(). Doch da wir ja auch Objekt-Eigenschaften einrichten müssen, da die Objekte an sich vielleicht mehr als nur einmal erzeugt werden müssen, ist es besser, sich nach einer bequemeren und kompakteren Methode umzusehen, die in eine einzige Codezeile implementiert werden könnte.

Zur Erzeugung des Hintergrunds und der Kopfzeile für das Info-Panel verwenden wir das grafische Objekt 'Bearbeiten'. Schreiben wir also die CreateEdit() Funktion:

//+------------------------------------------------------------------+
//| 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"
     }
  }

Jetzt kann das grafische Objekt 'Bearbeiten' (OBJ_EDIT) mittels einer einzigen Codezeile erzeugt werden. Wenn wir eine Funktion erzeugen, die das Info-Pabel auf das Chart setzt, werden wir es durch ein Beispiel veranschaulichen.

Gehen wir jetzt weiter zu den 'Text-Kennung' Objekten, die zur Anzeige der Liste der Position-Eigenschaften und ihrer Werte verwendet werden, und erzeugen die CreateLabel() Funktion auf ähnliche Weise:

//+------------------------------------------------------------------+
//| 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"
     }
  }

Auch hier ist es empfehlenswert, sich die Funktionsbeschreibungen in den MQL5 Referenzhinweisen anzusehen.

Wird sie vom Chart gelöscht wird, muss der Expert Advisor seinerseits alle Objekte löschen, die dem Chart zuvor hinzugefügt wurden. Dies geschieht, indem der Name des Objekts einfach an die DeleteObjectByName() Funktion übertragen wird. Sei sucht dann nach dem per Namen angegebenen Objekt und löscht es (falls es gefunden werden kann) mit Hilfe der eingebauten ObjectFind() Funktion, die nach Objekten sucht und der ObjectDelete() Funktion, die Objekte löscht

//+------------------------------------------------------------------+
//| 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()));
        }
     }
  }

In der DeleteObjectByName() Funktion implementieren wir darüberhinaus auch eine Prüfung nach Fehlern beim Löschen eines Objekts. Sollte ein Fehler auftreten, erscheint eine entsprechende Meldung samt Fehlercode und Beschreibung. Wie Sie im oben stehenden Code sehen können, verwenden wir eine zusätzliche, benutzerdefinierte Funktion, die den Fehlercode in eine Textbeschreibung umwandelt - die Funktion ErrorDescription(). Da es eine Menge Fehlercodes gibt, erkläre ich den obigen Code nur mittels Verwendung eines Teils dieser Funktion (vgl. Code unten). Die vollständige Version des Codes findet sich in der diesem Beitrag angehängten Quellcode-Datei.

//+------------------------------------------------------------------+
//| 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);
  }

Im vorangegangenen Beitrag haben wir uns mit der GetPositionProperties() Funktion beschäftigt, die Position-Eigenschaften abruft. Diesmal ist die Struktur der Funktion jedoch etwas komplexer. Wir suchen ach einer Position, die derzeit offen ist, wobei der Marker vorhanden/nicht vorhanden einer offenen Position in der globalen Variable pos_open abgelegt wird Diese Information ist vielleicht für andere Funktionen notwendig, ohne dass man die Funktion PositionSelect() jedes Mal aufrufen muss.

Wenn eine offene Position vorhanden ist, erhalten wir dann ihre Eigenschaften. Ansonsten weren alle Variablen genullt. Schreiben wir also jetzt eine einfache ZeroPositionProperties() Funktion:

//+------------------------------------------------------------------+
//| 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;
  }

Am Ende der GetPositionProperties() Funktion werden wir zudem eine benutzerdefinierte Funktion aufrufen, SetInfoPanel(), die das Info-Panel in das Chart zeichnet/aktualisiert.

//+------------------------------------------------------------------+
//| 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
  }

Schreiben wir jetzt also die SetInfoPanel() Funktion. Unten steht der Code mit detaillierten Anmerkungen:

//+------------------------------------------------------------------+
//| 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
  }

Sehen wir uns die SetInfoPanel() Funktion genauer an. Die Variablen, die mit den Eigenschaften der grafischen Objekte zu tun haben (Koordinaten, Farbe, Schriftart, angezeigter Text, usw.) werden zu Anfang der Funktion deklariert. Beachten Sie den Vorgang der Befüllung des Arrays mit den Y-Koordinaten für die Liste der Position-Eigenschaften auf dem Info-Panel. Er wird auf eine Art und Weise implementiert, die für Anfänger klar sein sollte. Doch verwendet man eine Schleife, kann er auf einige wenige Codezeilen reduziert werden. Sie können ihn folgendermaßen schreiben:

//--- 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;
     }

Anschließend müssen alle Eigenschaften der Objekte, die auf dem Panel angezgit weren sollen in den Parametern der zuvor erzeugten CreateLabel() und CreateEdit() Funktionen spezifizeirt werden, wobei man jeweils ein Objekt nach dem anderen hernimmt. Mit Hilfe der Schleife kann die gesamte Liste auch in einigen wenigen Codezeilen implementiert werden. Dazu müssen wir ein anderes Array für Objekte anlegen, das den Text der Namen der Position-Eigenschaften im Chart anzeigt. Das habe ich als Hausaufgabe für Sie.

Die GetPropertyValue() Funktion, die Anzahl der Objekte erhält, liefert den Wert, der dann an die CreateLabel() Funktion als vierter Parameter übertragen wird (angezeigter Text). Dies gilt für alle Objekte, die Werte der Position-Eigenschaften anzeigen werden. Der von der Funktion gelieferte Wert ist ein angepasster String-Wert, der letztlich auf dem Panel angezeigt wird. Unten steht der Code der Funktion mit detaillierten Anmerkungen:

//+------------------------------------------------------------------+
//| 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);
  }

Der Code oben schlägt vor, dass für jede an die Funktion übertragene Zahl ein bestimmter Wert vorbereitet wird, vorausgesetzt es gibt überhaupt eine offene Position. Gibt es derzeit keine offene Position, liefert die Funktion einen Bindestrich (-), der für alle Objekte, die mit Werten von Position-Eigenschaften zu tun haben, angezeigt wird.

Am Ende der SetInfoPanel() Funktion rufen wir die Funktion ChartRedraw() auf, die für eine erzwungene Neuzeichnung des Charts gedacht ist. Wenn sie nicht aufgerufen wird, werden Sie die gemachten Änderungen nicht sehen können.

Jetzt müssen wir eine Funktion schreiben, die alle vom Expert Advisor erzeugten grafischen Objekte löscht. Nennen wir sie mal 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
  }

Jetzt müssen wir nur noch die Methoden, die wir unter den Hauptfunktionen des Expert Advisors, die ursprünglich im Template nach seiner Erstellung mittels des MQL5-Assistenten vorhanden waren, verteilen. Das ist die leichteste Übung:

//+------------------------------------------------------------------+
//| 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();

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

Das einzige worüber Sie vielleicht stolpern könnten, ist die GetDeinitReasonText() Funktion, die eine Text-Beschreibung des Codes für den Grund der De-Initialisierung liefert:

//+---------------------------------------------------------------------+
//| 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;
  }

Wenn Sie versuchen, den Expert Advisor auf dem Chart-Symbol zu verwenden, das derzeit keine offene Position hat, sehen Sie auf dem Panel Bindestriche anstelle der Werte der Position-Eigenschaften. Wenn Sie eine bestimmte Position schließen, sieht das Panel ganz genauso aus.

Abb. 2 Info-Panel wenn keine offene Position vorhanden ist

Abb. 2 Info-Panel wenn keine offene Position vorhanden ist

Wird der Expert Advisor dem Chart des Symbols mit einer offenen Position hinzugefügt, oder wird eine Position geöffnet, nachdem der Expert Advisor dem Chart hinzugefügt wurde, werden alle Bindestriche durch die entsprechenden Werte der Position-Eigenschaft ersetzt.

Abb. 3 Das Info-Panel zeigt die Eigenschaften der offenen Position.

Abb. 3 Das Info-Panel zeigt die Eigenschaften der offenen Position.

Es gibt da noch eine kleine Besonderheit. Nachdem eine Position geschlossen wird, werden ihre Werte auf dem Panel erst dann aktualisiert, wenn eine neue Kursschwankung auftritt. Es gibt zwar eine Möglichkeit, die Werte sofort aktualisieren zu lassen, doch was für diese Implementierung erforderlich ist, soll Gegenstand des nächsten Beitrags aus dieser Reihe sein.

 

Fazit

Einige der in diesem Beitrag vorgestellten Funktionen werden auch in den nächsten Beiträgen der MQL5 Cookbook-Reihe verwendet. Andere hingegen werden, je nach anstehender Aufgabe, modifiziert und verbessert. Es empfiehlt sich daher die Beiträgen der Reihe nach zu lesen, einen nach dem anderen, da jeder neue Beitrag die logische Fortsetzung des vorangegangenen ist. Das alles hängt natürlich auch von Ihrer Kompetenz und dem Grad Ihrer Fähigkeiten ab, daher ist es vermutlich vernünftiger und interessanter mit den neueren Beiträgen zu beginnen. 

Die Quellcode-Datei ist an diesen Beitrag angehängt.