Orders.Erstellen aktiver MQL5-Bedienfelder für den Handel

Евгений | 4 März, 2016

Einleitung

Zweckdienlichkeit spielt eine entscheidende Rolle bei der Arbeit, insbesondere bei der Arbeit eines Börsenhändlers, bei der Schnelligkeit und Genauigkeit ausschlaggebend sein können. Bei der Vorbereitung des Arbeitsplatzes versucht jeder, ihn so einzurichten, dass er den eigenen Anforderungen am besten genügt, um Analysen in möglichst kurzer Zeit abzuschließen und in den Markt einzusteigen. Aber die Wirklichkeit sieht anders aus, die Entwickler können nicht alle zufrieden stellen, und einige Funktionen lassen sich nicht bis ins Kleinste individuell einstellen.

So ist zum Beispiel für Spekulanten, die auf minimale Kursänderungen, Pips, setzen, jeder Sekundenbruchteil für die Betätigung der Schaltfläche „Neuer Auftrag“ ungemein wichtig, und die anschließende Einstellung aller Parameter kann sich als zeitlich entscheidend erweisen.

Wie gehen wir damit um? Die Lösung liegt in der Erstellung eigener Bedienfelder, denn in MetaTrader 5 gibt es so wunderbare Instrumente wie „Schaltflächen“, „Eingabefelder“ und „Grafische Symbole“. Damit wollen wir uns hier beschäftigen.


2. Steuerfunktionen

Zunächst müssen wir klären, mit welchen Funktionen unser Bedienfeld ausgestattet sein soll: Das Hauptaugenmerk liegt auf dem Handel mithilfe unseres Bedienfeldes, weswegen es über folgende Funktionen verfügen muss:

Es würde ebenfalls nicht schaden, die Möglichkeit zur Einstellung des Farbmodells für das Pult sowie der Schriftgröße und der Speicherung der Parameter hinzuzufügen. Wir werden die Elemente unseres künftigen Bedienfeldes eingehend darstellen. Zunächst geben wir für jede seiner Funktionen die Bezeichnung des entsprechenden Objekts, seine Art und seine Zweckbestimmung an. Jede Objektbezeichnung beginnt mit der Zeichenfolge „ActP“, dabei handelt es sich um ein eindeutiges Zeichen dafür, dass dieses Objekt zu dem Bedienfeld gehört.

2,1. Die Eröffnung einer Position

Hier werden die zur Eröffnung einer Position erforderlichen Parameter eingeführt, die Eröffnung selbst erfolgt durch Betätigung einer Schaltfläche. Zur Einrichtung der Stop Loss- und Take Profit-Grenzen benutzen wir Hilfslinien, die durch ein Kontrollkästchen aktiviert werden. Die Auswahl der Art der Ausführung erfolgt mithilfe einer Gruppe von Optionsschaltflächen.

Bezeichnung
Art
Beschreibung
 ActP_buy_button1  Schaltfläche
 Schaltfläche für einen Kaufvorgang
 ActP_sell_button1
 Schaltfläche
 Schaltfläche für einen Verkaufsvorgang
 ActP_DealLines_check1
 Kontrollkästchen
 Häkchen zum Setzen/Entfernen der Hilfslinien
 ActP_Exe_radio1
 Optionsschaltfläche
 Gruppe von Optionsschaltflächen zur Auswahl der Art der Ausführung des Vorgangs
 ActP_SL_edit1
 Eingabefeld
 Feld zur Eingabe der Stop Loss-Grenze
 ActP_TP_edit1
 Eingabefeld
 Feld zur Eingabe der Take Profit-Grenze
 ActP_Lots_edit1
 Eingabefeld
 Feld zur Eingabe des Umfangs
 ActP_dev_edit1
 Eingabefeld
 Feld zur Eingabe der zulässigen Abweichung bei der Eröffnung
 ActP_mag_edit1
 Eingabefeld
 Feld zur Eingabe der „magischen Zahl“
 ActP_comm_edit1  Eingabefeld  Feld zur Eingabe eines Kommentars

Tabelle 1 Übersicht über die Elemente des Bedienfeldes für die „Eröffnung eines Handelsvorgangs“

2.2 Platzieren einer Pending Order

Es folgen die zur Platzierung eines bedingten Auftrags erforderlichen Parameter, die Platzierung selbst erfolgt durch Betätigung einer Schaltfläche. Zur Einrichtung der Stop Loss-, Take Profit- und Stoplimit-Grenzen sowie der Ablaufzeit benutzen wir Hilfslinien, die durch Kontrollkästchen aktiviert werden. Die Auswahl der Art der Ausführung und der Art der Ablaufzeit erfolgt mithilfe einer Gruppe von Optionsschaltflächen.

Bezeichnung
Art
Beschreibung
 ActP_buy_button2  Schaltfläche
 Schaltfläche zur Platzierung eines Kaufauftrags
 ActP_sell_button2
 Schaltfläche
 Schaltfläche zur Platzierung eines Verkaufsauftrags
 ActP_DealLines_check2
 Kontrollkästchen
 Häkchen zum Setzen/Entfernen der Hilfslinien
 ActP_lim_check2  Kontrollkästchen  Häkchen zum Setzen/Entfernen eines Auftrags mit Stoplimit 
 ActP_Exe_radio2
 Optionsschaltfläche
 Gruppe von Optionsschaltflächen zur Auswahl der Art der Ausführung des Vorgangs
 ActP_exp_radio2  Optionsschaltfläche  Gruppe von Optionsschaltflächen zur Auswahl der Art der Ablaufzeit für den Vorgang
 ActP_SL_edit2
 Eingabefeld
 Feld zur Eingabe der Stop Loss-Grenze
 ActP_TP_edit2
 Eingabefeld
 Feld zur Eingabe der Take Profit-Grenze
 ActP_Lots_edit2
 Eingabefeld
 Feld zur Eingabe des Umfangs
 ActP_limpr_edit2
 Eingabefeld  Feld zur Eingabe des Kurses für einen Auftrag mit Stoplimit
 ActP_mag_edit2
 Eingabefeld
 Feld zur Eingabe der „magischen Zahl“
 ActP_comm_edit2  Eingabefeld  Feld zur Eingabe eines Kommentars
 ActP_exp_edit2  Eingabefeld  Feld zur Eingabe der Ablaufzeit
 ActP_Pr_edit2  Eingabefeld  Feld zur Eingabe des Kurses für die Ausführung des Auftrags 

 Tabelle 2 Übersicht über die Elemente des Bedienfeldes zur „Platzierung von Pending Orders“

2,3. Ändern/Schließen von Vorgängen

Hier haben wir die zum Ändern und Schließen von Vorgängen erforderlichen Parameter. Zur Einrichtung der Stop Loss- und Take Profit-Grenzen benutzen wir Hilfslinien, die durch ein Kontrollkästchen aktiviert werden. Die Auswahl der Handelsvorgänge erfolgt aus der entsprechenden aufklappenden Liste.

Bezeichnung
Art
Beschreibung
 ActP_ord_button5   Aufklappende Liste   Liste zur Auswahl eines Handelsvorgangs
 ActP_mod_button4  Schaltfläche
 Schaltfläche zur Änderung des Vorgangs 
 ActP_del_button4
 Schaltfläche
 Schaltfläche zum Schließen des Vorgangs 
 ActP_DealLines_check4
 Kontrollkästchen
 Häkchen zum Setzen/Entfernen der Hilfslinien
 ActP_SL_edit4
 Eingabefeld
 Feld zur Eingabe der Stop Loss-Grenze
 ActP_TP_edit4
 Eingabefeld
 Feld zur Eingabe der Take Profit-Grenze
 ActP_Lots_edit4
 Eingabefeld
 Feld zur Eingabe des Umfangs 
 ActP_dev_edit4
 Eingabefeld
 Feld zur Eingabe der zulässigen Abweichung 
 ActP_mag_edit4
 Eingabefeld
 Feld zur Anzeige der „magischen Zahl“ (nur Lesezugriff)
 ActP_Pr_edit4  Eingabefeld  Feld zur Anzeige des Eröffnungskurses (nur Lesezugriff)

Tabelle 3. Übersicht über die Elemente des Bedienfeldes zum „Ändern/Schließen von Vorgängen“

2,4. Ändern/Entfernen von Aufträgen

Kommen wir jetzt zu den für die Änderung und Löschung von Aufträgen erforderlichen Parametern. Zur Einrichtung der Stop Loss-, Take Profit- und Stoplimit-Grenzen sowie der Ablaufzeit benutzen wir Hilfslinien, die durch Kontrollkästchen aktiviert werden. Die Auswahl der Art der Ablaufzeit erfolgt mithilfe einer Gruppe von Optionsschaltflächen. Die Auswahl der Aufträge erfolgt anhand der entsprechenden aufklappenden Liste.

Bezeichnung
Art
Beschreibung
 ActP_ord_button5  Aufklappende Liste  Liste zur Auswahl eines Auftrags
 ActP_mod_button3  Schaltfläche
 Schaltfläche zur Änderung des Auftrags 
 ActP_del_button3
 Schaltfläche
 Schaltfläche zur Löschung des Auftrags 
 ActP_DealLines_check3
 Kontrollkästchen
 Häkchen zum Setzen/Entfernen der Hilfslinien
 ActP_exp_radio3  Optionsschaltfläche  Gruppe von Optionsschaltflächen zur Auswahl der Art des Ablaufzeit des Auftrags
 ActP_SL_edit3
 Eingabefeld
 Feld zur Eingabe der Stop Loss-Grenze
 ActP_TP_edit3
 Eingabefeld
 Feld zur Eingabe der Take Profit-Grenze
 ActP_Lots_edit3
 Eingabefeld
 Feld zur Anzeige des Umfangs (nur Lesezugriff)
 ActP_limpr_edit3
 Eingabefeld  Feld zur Eingabe des Kurses für einen Auftrag mit Stoplimit 
 ActP_mag_edit3
 Eingabefeld
 Feld zur Anzeige der „magischen Zahl“ (nur Lesezugriff)
 ActP_comm_edit3  Eingabefeld  Feld zur Eingabe eines Kommentars
 ActP_exp_edit3  Eingabefeld  Feld zur Eingabe der Ablaufzeit
 ActP_Pr_edit3  Eingabefeld  Feld zur Eingabe der Kurses für die Ausführung des Auftrags 
 ActP_ticket_edit3  Eingabefeld  Feld zur Anzeige des Händlerzettels zu dem Auftrag (nur Lesezugriff) 

Tabelle 4. Übersicht über die Elemente des Bedienfeldes zum „Ändern/Entfernen von Aufträgen“

2.5 Einstellungen

Hier werden die Einstellungen für die Farbe der Schaltflächen, Beschriftungen sowie der Schrift der aufklappenden Liste und die Schriftgröße festgelegt.

Bezeichnung
Art
Beschreibung
 ActP_col1_button6  Aufklappende Liste
 Liste zur Auswahl der Schaltflächenfarbe
 ActP_col2_button6
 Aufklappende Liste
 Liste zur Auswahl der Farbe der Beschriftungen
 ActP_col3_button6
 Aufklappende Liste
 Liste zur Auswahl der Schriftfarbe
 ActP_font_edit6
 Eingabefeld
 Feld zur Festlegung der Schriftgröße

Tabelle 5. Übersicht über die Elemente des Bedienfeldes „Einstellungen“

Außerdem fügen wir eine Schaltfläche hinzu, mit der das Bedienfeld verborgen werden kann, wenn es nicht benötigt wird. Sie werden wahrscheinlich bemerkt haben, dass es hier das Werkzeug „Hilfslinien“ gibt. Aber worum handelt es sich dabei, und wozu werden sie gebraucht? Mithilfe dieser Linien können die Stop Loss- und die Take Profit-Grenze, den Kurs für die Auslösung einer Pending Order, den Kurs für den Auftrag mit Stoplimit (waagerechte Linien) sowie die Zeit bis zum Ablaufen eines bedingte Auftrags (einer Pending Order) (senkrechte Linie) festgelegt werden, indem man sie einfach mit der Maus auf den gewünschten Kurs/die gewünschte Zeit zieht.

Die bildliche Festlegung weniger aufwendig als die in Textform (bei der der Kurs/die Zeit von Hand in die entsprechenden Felder eingegeben werden müssen). Außerdem dienen uns diese Linien zum „Hervorheben“ der Parameter des ausgewählten Auftrags, denn wenn zahlreiche Aufträge vorhanden sind, können die in den üblichen Ausgabeprogrammen zur Darstellung der Kurse verwendeten gestrichelten Linien Verwirrung stiften. 


3. Das allgemeine Vorgehen beim Anlegen einer Benutzeroberfläche

Das ist also das erklärte Ziel: die Schaffung eines grafischen Hilfsmittels für den Handel. Dafür benötigen wir eine möglichst leicht zu handhabende Benutzeroberfläche. Zunächst sei klargestellt, dass alle Steuerelemente (und davon gibt es viele) programmiert, das heißt, die Lage und Größe der Objekte im Voraus ermittelt werden müssen.

Stellen Sie sich jetzt einmal vor, Sie hätten die Objektkoordinaten langwierig, mühsam und verbissen berechnet, damit sie einander nicht überlagern und gut sichtbar sind, und dann ergibt sich plötzlich die Notwendigkeit, ein weiteres Objekt hinzuzufügen, und unser ganzes schönes Modell ist für die Katz.

Wer sich mit schnellen Programmentwicklungsumgebungen (Delphi, C++ Builder usw.) auskennt, weiß wie schnell dort selbst die komplizierteste Benutzeroberfläche erstellt werden kann.

Wir versuchen, etwas Ähnliches in MQL5 umzusetzen. Zu Beginn platzieren wir die Steuerungsobjekte ganz einfach mit der Maus und passen ihre Größe an. Anschließend schreiben wir ein einfaches Skript, um die Eigenschaften aller Objekte in dem Diagramm auszulesen und in einer Datei zu speichern, sodass wir sie gegebenenfalls einfach abrufen und die Objekte in anderen Diagrammen vollständig wiederherstellen können.

Der Skriptcode kann etwa so aussehen:

//+------------------------------------------------------------------+
//|                                  Component properties writer.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

input int interfaceID=1; //input parameter - the identifier of the stored interface
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //Open file for writing  
   int handle=FileOpen("Active_Panel_scheme_"+IntegerToString(interfaceID)+".bin", FILE_WRITE|FILE_BIN);
   if(handle!=INVALID_HANDLE)
     {
      //We will go all the objects on the chart
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //And write their properties in the file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
      Alert("Done!");
     }
  }
//+------------------------------------------------------------------+

Die Programmierung ist erkennbar simpel, alle Objekte werden auf das Diagramm übertragen und ihre Eigenschaften in eine binäre Datei geschrieben. Beim Auslesen der Datei kommt es da darauf an, dass die Reihenfolge der aufgezeichneten Eigenschaften beibehalten wird. 

Damit ist das Skript fertig, und wir kommen zum Anlegen der Benutzeroberfläche.

Und wir beginnen wie beim Hauptmenü mit dem Aufbau anhand von Registerkarten. Weshalb brauchen wir die? Weil die Anzahl der Objekte groß ist, und es schwierig wäre, sie alle auf einmal anzuzeigen. Da die Objekte bereits ihrem jeweiligen Zweck entsprechend gruppiert wurden (s. die Tabellen oben), ist es umso einfacher jede Gruppe unter einer eigenen Registerkarte unterzubringen.   

So legen wir im Menü der Anwendung auf dem Ausgabegerät unter Einfügen -> Objekte -> Grafische Objekte ganz oben im Diagramm fünf Schaltflächen an, die als unser Hauptmenü dienen werden.

Abbildung 1. Registerkarten des Bedienfeldes

Abb. 1 Registerkarten des Bedienfeldes

Vergessen Sie dabei nicht, dass die Objekte leicht zu kopieren sind, indem das benötigte ausgewählt und mit gedrückter Steuerungstaste (Strg bzw. Ctrl) mit der Maus an die gewünschte Stelle gezogen wird. Dort wird dann eine Kopie des gewünschten Objekts abgelegt.

Besondere Aufmerksamkeit gilt dabei den Objektbezeichnungen, die, wie wir wissen, ausnahmslos mit „ActP“ beginnen müssen. Außerdem fügen wir der Bezeichnung ein „main“ hinzu, um deutlich zu machen, dass das Objekt zum Hauptmenü des Bedienfeldes gehört.

Abbildung 2. Objektverzeichnis (Registerkarten des Bedienfelds)

Abbildung 2. Objektverzeichnis (Registerkarten des Bedienfelds)

Auf ähnliche Weise „wenden“ wir auch den Inhalt der Registerkarten auf neue Bildelemente an. Der Inhalt der einzelnen Registerkarten muss jeweils in einem eigenem Bildelement untergebracht werden!

Registerkarte „Börse“:

Abbildung 3. Elemente der Registerkarte „Börse“

Abbildung 3. Elemente der Registerkarte „Börse“

Registerkarte „Pending“:

Abbildung 4. Elemente der Registerkarte „Pending“

Abbildung 4. Elemente der Registerkarte „Pending“

Registerkarte „Einstellungen“:

Abbildung 5. Elemente der Registerkarte „Einstellungen“

Abbildung 5. Elemente der Registerkarte „Einstellungen“

Die Registerkarte „Ändern/Schließen“ ist anders, sie dient der Änderung/Schließung bedingter Aufträge sowie der Änderung/Schließung von Handelsvorgängen. Es ist sinnvoll, die Verarbeitung der Handelsvorgänge und der Aufträge auf zwei getrennte Unterregisterkarten zu verteilen. Zunächst legen wir eine Schaltfläche zum Aufklappen der Liste an, aus der wir den Auftrag oder Handelsvorgang zur Bearbeitung auswählen.

Abbildung 6. Elemente der Registerkarte „Ändern/Schließen“

Abbildung6. Die Elemente der Registerkarte „Ändern/Schließen

Danach legen wir die Unterregisterkarten an. Zur Verarbeitung von Handelsvorgängen:

Abbildung 7. Unterregisterkarte zur Verarbeitung von Handelsvorgängen;

Abbildung 7. Unterregisterkarte zur Verarbeitung von Handelsvorgängen;

Und zur Verarbeitung von Aufträgen:

Abbildung 9. Unterregisterkarte zur Verarbeitung von Aufträgen

Abbildung 8. Unterregisterkarte zur Verarbeitung von Aufträgen

Das wär‘s, die Benutzeroberfläche ist fertig.

Wir wenden das Skript auf jedes der Bildelemente an, um jede Registerkarte in einer gesonderten Datei zu speichern. Dazu muss der Eingangsparameter „interfaceID“ bei jeder Registerkarte ein anderer sein:

Die Registerkarte 5 entspricht der Schaltfläche „Verbergen“ des Hauptmenüs, da sich unter dieser Registerkarte keine Objekte befinden, können wir sie übersprungen.

Nach diesen Umstellungen befinden sich unter „Programme -> MQL5 ->“ folgende Dateien im Dateiverzeichnis der Anwendung auf dem Ausgabegerät (Terminal):

Abbildung 9. Dateiverzeichnis der Bedienfeldmuster

Abbildung 9. Dateiverzeichnis der Bedienfeldmuster


4. Laden der Elemente der Benutzeroberfläche

Die Elemente der Benutzeroberfläche sind also in Dateien gespeichert und warten nur darauf, dass wir sie an die Arbeit lassen. Zunächst bestimmen wir die Stelle, an der unser Bedienfeld platziert werden soll.  Wenn es unmittelbar im Hauptdiagramm angezeigt werden soll, verdeckt es das Kursdiagramm, was nicht besonders günstig ist. Deshalb ist es ratsamer, das Bedienfeld in einem Unterfenster des Hauptdiagramms anzulegen. Das Unterfenster kann von einem Indikator angelegt werden.

Wir stellen ihn zusammen:

#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window //place the indicator in a separate window

int OnInit()
  {
//--- indicator buffers mapping
   //Set the short name of the indicator
   IndicatorSetString(INDICATOR_SHORTNAME, "AP");
//---
   return(0);
  }

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 value of prev_calculated for next call
   return(rates_total);
  }

Der Code ist überaus einfach, da die Grundfunktion dieses Indikators, die Erstellung von Unterfenstern keinerlei Berechnungen ausführen muss. Das Einzige, was zu tun ist, besteht in der Festlegung einer „Kurzbezeichnung“ für den Indikator, unter der wir ihn in dem Unterfenster finden können. Wenn wir ein Diagramm mit dem Indikator erstellen und aufrufen, erscheint sein Fenster.

Widmen wir uns jetzt dem Bedienfeld für ein Expert-System. Dazu legen wir ein neues Expert-System an.

Die Funktion OnInit() erhält folgende Operatoren:

double Bid,Ask;         //variables for current prices
datetime time_current;  //time of last tick
int wnd=-1;             //index of the window with an indicator
bool last_loaded=false; //flag indicating whether it's a first initialization or not
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   //Start the timer at intervals of 1 second
   EventSetTimer(1); 
   //Get the latest prices
   get_prices();
   //Define the window with an indicator
   wnd=ChartWindowFind(0,"AP");
   //If the first initialization - create interface
   if(!last_loaded) create_interface();
//---
   return(0);
  }

Hier starten wir einen Zeitgeber (Timer) (weshalb, wird weiter unten erläutert), rufen die letzten Börsenkurse ab, finden mithilfe der Funktion ChartWindowFind das Indikatorfenster und speichern es in einer Variablen. Die Auszeichnung (Flag) „last_loaded“ weist aus, ob es sich um den ersten Aufruf des Expert-Systems handelt oder nicht. Dadurch das erneute Laden der Benutzeroberfläche bei einer wiederholten Bereitstellung vermieden. 

Die Funktion create_interface() sieht folgendermaßen aus:

//+------------------------------------------------------------------+
//| Function of the interface creation                               |
//+------------------------------------------------------------------+
void create_interface()
  {
   //if reset settings is selected
   if(Reset_Expert_Settings)
     {
     //Reset
      GlobalVariableDel("ActP_buttons_color");
      GlobalVariableDel("ActP_label_color");
      GlobalVariableDel("ActP_text_color");
      GlobalVariableDel("ActP_font_size");
     }

   //Create the main menu interface
   ApplyScheme(0);
   //Create the interface tab "Market"
   ApplyScheme(1);
   //Set all objects as unmarked
   Objects_Selectable("ActP",false);
   //redraw the chart
   ChartRedraw();
  }

Vor allem anderen prüfen wir, ob der Eingangsparameter „Einstellungen zurücksetzen“ vorhanden ist, falls ja, löschen wir alle für die Einstellungen zuständigen globalen Variablen. Welche Auswirkungen das auf das Bedienfeld hat, sehen wir weiter unten. Des Weiteren erstellt die Funktion ApplyScheme() die Benutzeroberfläche aus einer Datei. 

//+------------------------------------------------------------------+
//| The function for the interface loading                           |
//| ID - ID of the saved interface                                   |
//+------------------------------------------------------------------+
bool ApplyScheme(int ID)
  {
   string fname="Active_Panel_scheme_custom_"+IntegerToString(ID)+".bin";
   //download the standard scheme if there isn't saved scheme 
   if(!FileIsExist(fname)) fname="Active_Panel_scheme_"+IntegerToString(ID)+".bin";
   //open file for reading
   int handle=FileOpen(fname,FILE_READ|FILE_BIN);
   //file opened
   if(handle!=INVALID_HANDLE)
     {
      //Loading all objects
      while(!FileIsEnding(handle))
        {
         string obj_name=FileReadString(handle,100);
         int _wnd=wnd;
         //the auxiliary lines are in the main window
         if(StringFind(obj_name,"line")>=0) _wnd=0;
         ENUM_OBJECT obj_type=FileReadInteger(handle);
         //creating object
         ObjectCreate(0, obj_name, obj_type, _wnd, 0, 0);   
         //and apply the properties 
         ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_XSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_COLOR,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STYLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_WIDTH,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BACK,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTED,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTABLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_READONLY,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STATE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,FileReadInteger(handle));

         ObjectSetString(0,obj_name,OBJPROP_TEXT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_FONT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,FileReadString(handle,100));

         ObjectSetDouble(0,obj_name,OBJPROP_PRICE,FileReadDouble(handle));

         //Set color for the objects
         if(GlobalVariableCheck("ActP_buttons_color") && obj_type==OBJ_BUTTON)
            ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,GlobalVariableGet("ActP_buttons_color"));
         if(GlobalVariableCheck("ActP_label_color") && obj_type==OBJ_LABEL)
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_label_color"));
         if(GlobalVariableCheck("ActP_text_color") && (obj_type==OBJ_EDIT || obj_type==OBJ_BUTTON))
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_text_color"));
         if(GlobalVariableCheck("ActP_font_size") && (obj_type==OBJ_EDIT || obj_type==OBJ_LABEL))
            ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,GlobalVariableGet("ActP_font_size"));
         //Set global variable font size
         if(obj_name=="ActP_font_edit6" && GlobalVariableCheck("ActP_font_size"))
            ObjectSetString(0,obj_name,OBJPROP_TEXT,IntegerToString(GlobalVariableGet("ActP_font_size")));
        }
      //Close file
      FileClose(handle);
      return(true);
     }
   return(false);
  }

Daran ist wieder nichts kompliziert. Die Funktion öffnet die erforderliche Datei mit der zuvor angelegten Musterbenutzeroberfläche und legt sie in dem angegebenen Fenster an, das wir zuvor festgelegt haben (das Indikatorfenster). Außerdem wählen wir aus den globalen Variablen der Anwendung auf dem Ausgabegerät die Farbe der Objekte und die Schriftgröße aus. 

Die Funktion Objects_Selectable() sperrt mit Ausnahme der Hilfslinien alle Objekte, um die Schaltflächenanimation einzuschalten und nicht versehentlich irgendein Objekt zu löschen.

//+------------------------------------------------------------------+
//| Function of setting objects as unselectable                      |
//+------------------------------------------------------------------+
void  Objects_Selectable(string IDstr,bool flag)
  {
   //Check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel
      if(StringFind(n,IDstr)>=0)
        {
         //Lines remain untouched
         if(!flag)
            if(StringFind(n,"line")>-1) continue; 
         //Set everything unselectable except the lines
         ObjectSetInteger(0,n,OBJPROP_SELECTABLE,flag); 
        }
     }
  }

Kommen wir nun zu der Funktion OnTick(). Sie dient uns ausschließlich zum Bezug der aktuellen Börsenkurse.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //Get the latest prices
   get_prices();
  }

So sieht die Funktion get_prices() aus:

//+------------------------------------------------------------------+
//| Function obtain information on tick                              |
//+------------------------------------------------------------------+
void get_prices()
  {
   MqlTick tick;
   //if the tick was
   if(SymbolInfoTick(Symbol(),tick))
     {
      //obtain information
      Bid=tick.bid;
      Ask=tick.ask;
      time_current=tick.time;
     }
  }

Und nicht zu vergessen OnDeinit():

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   //if the deinitialisation reason isn't the timeframe or symbol change
   if(reason!=REASON_CHARTCHANGE)
     {
      //reset initialization flag
      last_loaded=false;  
      //Delete all panel objects
      ObjectsDeleteAll_my("ActP");
      //Delete files with the saved state of the tabs
      FileDelete("Active_Panel_scheme_custom_1.bin");
      FileDelete("Active_Panel_scheme_custom_2.bin");
      FileDelete("Active_Panel_scheme_custom_3.bin");
      FileDelete("Active_Panel_scheme_custom_4.bin");
      FileDelete("Active_Panel_scheme_custom_5.bin");
     }
   //otherwise set a flag
   else last_loaded=true;
   //stop the timer
   EventKillTimer();
  }

Zunächst prüfen wir den Grund für die Bereinigung, wenn es sich dabei um einen Wechsel des Zeitraums und/oder des Kürzels handelt, werden die Bedienfeldelemente nicht gelöscht. In allen anderen Fällen wird mithilfe der Funktion ObjectsDeleteAll_my() alles gelöscht.

//+------------------------------------------------------------------+
//| The function deletes all panel objects                           |
//| IDstr - object identifier                                        |
//+------------------------------------------------------------------+
void  ObjectsDeleteAll_my(string IDstr)
  {
   //check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //if the name contains the identifier - remove the object
      if(StringFind(n,IDstr)>=0) ObjectDelete(0,n);
     }
  }

Nach dem Zusammenstellen und Starten des Expert-Systems erhalten wir folgendes Ergebnis:

Abbildung 10. Beispiel für ein Expert-System

Abbildung 10. Beispiel für ein Expert-System

Aber was nützt alle Schönheit, solange wir nicht versuchen, die Objekte dazu zu bringen, auf unsere Veränderungen an ihnen zu reagieren.


5. Die Verarbeitung von Ereignissen

Die Benutzeroberfläche ist fertig, jetzt müssen wir sie ans Laufen bringen. Alle Operationen mit Objekten erzeugen bestimmte Ereignisse. Die Funktion OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) ist die Verarbeitungsroutine für die Ereignisgruppe ChartEvent . Von allen Ereignissen interessieren uns die folgenden: 

In unserem Beispiel verweist der Parameter der Funktion „id“ auf den Bezeichner des Ereignisses und „sparam“ auf die Bezeichnung des das Ereignis hervorrufenden Objektes, alle übrigen Parameter interessieren uns nicht.

Somit ist das erste Ereignis, mit dem wir uns befassen, das Anklicken einer Schaltfläche des Hauptmenüs.

5,1. Die Verarbeitung vor Ereignissen des Hauptmenüs

Ich erinnere daran, dass das Hauptmenü fünf Schaltflächen umfasst. Wird eine von ihnen angeklickt, wechselt sie in die „gedrückte“ Stellung, und die Benutzeroberfläche aus der entsprechenden Registerkarte wird geladen. Danach müssen alle übrigen Schaltflächen des Menüs anklickbar bleiben.

//+------------------------------------------------------------------+
//| Event handlers                                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //main menu button click
      if(sparam=="ActP_main_1") {Main_controls_click(1); ChartRedraw(); return;}
       //Here we execute the corresponding operators
      if(sparam=="ActP_main_2") {Main_controls_click(2); ChartRedraw(); return;}
      if(sparam=="ActP_main_3") {Main_controls_click(3); ChartRedraw(); return;}
      if(sparam=="ActP_main_4") {Main_controls_click(4); ChartRedraw(); return;}
      if(sparam=="ActP_main_5") {Main_controls_click(5); ChartRedraw(); return;}
   ...   
   }
...
}

Nach Anklicken einer Menüschaltfläche wird die Funktion Main_controls_click() ausgeführt. Das Diagramm wird mithilfe der Funktion ChartRedraw() neu gezeichnet und die Ausführung der Funktion abgeschlossen. Wir müssen die Ausführung der beenden, weil stets nur ein Objekt angeklickt werden kann, weswegen die weitere Ausführung der Funktion lediglich Prozessorkapazität verschwenden würde.

//+------------------------------------------------------------------+
//| Tab processor                                                    |
//| ID - index of clicked tab                                        |
//+------------------------------------------------------------------+
void Main_controls_click(int ID)
  {
   int loaded=ID;
   //we will go all tabs
   for(int i=1;i<6;i++)
     {
      //for all except the selected set inactive
      if(i!=ID) 
        {
         //also remember the last active tab
         if(ObjectGetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE)==1) loaded=i;
         ObjectSetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE,0);
        }
     }
//if(loaded==ID) return;
   //set an active state for the selected
   ObjectSetInteger(0,"ActP_main_"+IntegerToString(ID),OBJPROP_STATE,1);
   //delete the drop-down lists
   DeleteLists("ActP_orders_list_"); 
   DeleteLists("ActP_color_list_");
   //and set the list buttons to the unpressed state
   ObjectSetInteger(0,"ActP_ord_button5",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col1_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col2_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col3_button6",OBJPROP_STATE,0);
   //save state of the last active tab
   SaveScheme(loaded);
   //remove old tab
   DeleteScheme("ActP");
   //and load a new
   ApplyScheme(ID);
   //Set all objects as unselected
   Objects_Selectable("ActP",false);
  }

Die Funktionen Objects_Selectable() und ApplyScheme() haben wir bereits kennen gelernt, und auf die Funktion DeleteLists() kommen wir später noch zu sprechen.

Die Funktion SaveScheme() speichert die Benutzeroberfläche in eine Datei, damit die Objekte bei einem erneuten Laden noch dieselben Eigenschaften aufweisen:

//+------------------------------------------------------------------+
//| Interface saving function                                        |
//+------------------------------------------------------------------+
void SaveScheme(int interfaceID)
  {
   //open file for writing
   int handle=FileOpen("Active_Panel_scheme_custom_"+IntegerToString(interfaceID)+".bin",FILE_WRITE|FILE_BIN);
   //if file opened
   if(handle!=INVALID_HANDLE)
     {
      //go all the chart objects
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //if the object belongs to the panel
         if(StringFind(name,"ActP")<0) continue;
         //and it isn't a tab
         if(StringFind(name,"main")>=0) continue; 
         //write the object properties to a file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
     }
  }

Mit der Funktion DeleteScheme() werden die Registerkartenobjekte entfernt.

//+------------------------------------------------------------------+
//| Function to delete all the panel objects, except tabs            |
//+------------------------------------------------------------------+
void  DeleteScheme(string IDstr)
  {
   //we will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //remove everything but the tab
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

Durch die Ausführung der Funktion Main_controls_click() entfernen wir als die „alte“ Registerkarte, nachdem wir sie zuvor gespeichert haben, und laden eine neue.

Nach dem Zusammenstellen des Expert-Systems werden wir die Ergebnisse sehen.

Jetzt laden wir mit einem Klick auf eine der Schaltflächen des Hauptmenüs neue Registerkarten, während wir den Zustand der alten speichern.

Abbildung 11. Die Elemente der Registerkarte „Pending“

Abbildung 11. Die Elemente der Registerkarte „Pending“

Abbildung 12. Die Elemente der Registerkarte „Ändern/Schließen“

Abbildung 12. Die Elemente der Registerkarte „Ändern/Schließen“


Abbildung 12. Elemente der Registerkarte „Einstellungen“

Abbildung 13. Elemente der Registerkarte „Einstellungen“

Damit kann die Verarbeitung von Ereignissen des Hauptmenüs abgeschlossen werden, da es seine Funktionen jetzt in vollem Umfang erfüllt.

5,2. Die Verarbeitung von Ereignissen der Kontrollkästchenkomponente

Die Festlegung der Hilfslinien und der Aufträge mit Stoplimit erfolgt mithilfe der Komponente „Kontrollkästchen“, aber eine solche ist in dem Verzeichnis der grafischen Objekte von MT5 nicht vorhanden, also müssen wir sie erstellen. Es gibt das Objekt „grafisches Symbol“, dabei handelt es sich faktisch um eine Abbildung, die den Zustand „Ein“ oder „Aus“ aufweisen kann. Der Zustand kann mit einem Klick auf das Objekt geändert werden. Jedem Zustand ist jeweils ein eigenes „Bild“ zuzuordnen. Wir entscheiden uns für folgende Abbildungen:

Jetzt legen wir diese Bildelemente in den Eigenschaften des Objektes an:

Abbildung 14. Festlegung der Eigenschaften des Elements „Kontrollkästchen“

Abbildung 14. Festlegung der Eigenschaften des Elements „Kontrollkästchen“

Ich erinnere daran, dass sich die Bilder, um in dem Verzeichnis zugänglich zu sein, in dem Ordner „Programme -> MQL5-> Bilder“ befinden und die Erweiterung „.bmp“ aufweisen müssen.

Wir nehmen uns der Verarbeitung eines beim Anklicken eines Objektes eintretenden Ereignisses an. Dazu verwenden wir als Beispiel das für die Festlegung der Hilfslinien bei Eröffnung eines Geschäftsvorgangs zuständige Kontrollkästchen.

      //click on the flag of the setting of auxiliary lines during transaction opening
      if(sparam=="ActP_DealLines_check1")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //If the flag is set
         if(selected)
         {
            //Retrieve the value of the stop loss and take profit from the input fields
            string SL_txt=ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT);
            string TP_txt=ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT);
            double val_SL, val_TP;
            
            //If the Stop field is not empty
            //save the value
            if(SL_txt!="")
               val_SL=StringToDouble(SL_txt);

            //if empty
            else
            {
               //Take the max. and min. prices of chart
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               //Set the stop at the 1/3 of the chart price range
               val_SL=pr_min+(pr_max-pr_min)*0.33;
            }   
            
            //Similarly processes the Take
            if(TP_txt!="")
               val_TP=StringToDouble(TP_txt);
            else
            {
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               val_TP=pr_max-(pr_max-pr_min)*0.33;
            }      
            //Move the line to new positions
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, val_SL);  
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, val_TP);  
         }
          //If the flag is unset
         else
         {
             //remove the lines
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, 0);
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, 0);
         }
          //redraw the chart
         ChartRedraw();
          //and finish the function 
         return;
      }

Auf ähnliche Weise wird auch mit den Kontrollkästchen zur Festlegung der Hilfslinien unter den Registerkarten zur „Platzierung einer Pending Order“ sowie zum „Ändern/Schließen eines Auftrags“ verfahren. Deshalb werden sie in diesem Beitrag nicht behandelt. Wer mag, kann sie sich im Programmcode des Expert-Systems ansehen.

Die Verarbeitungsroutine für das Kontrollkästchen zur Platzierung eines Auftrags mit Stoplimit unter der Registerkarte „Pending“ könnte etwa so aussehen:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...

   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //Click on the orders stoplimit check box
      if(sparam=="ActP_limit_check2")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected) //if flag is set
         {
            //set the new color for the price edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, White);
            //enable it for the edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, false);
            //установим в поле значение текущей цены
            //Set the current price as the field value
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, DoubleToString(Bid, _Digits));
            //if the auxiliary lines are allowed
            //move them
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, Bid);
         }  
          //if flag is unset
         else
         {
            //set the field unavailable for editing
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            //set the field color
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, true);
            //and "empty" text
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, "");
            //if the auxiliary lines are allowed
            //move them to the zero point
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, 0);
         }
      }       
   ...   
   }
...
}

Damit sind die Kontrollkästchen abgeschlossen. Kommen wir also zum nächsten selbst erstellten Objekt, der „Gruppe von Optionsschaltflächen“.

5,3. Die Verarbeitung von Ereignissen der „Gruppe von Optionsschaltflächen“

Mithilfe dieser Komponente können wir die Art der Abwicklung eines Handelsvorgangs sowie die Art der Ablaufzeit eines Auftrags auswählen. Wie bei den Kontrollkästchen verwenden wir grafische Elemente, jetzt jedoch mit anderen Abbildungen. 

Aber hier ist es deshalb komplizierter, weil beim Anklicken einer der Schaltflächen alle übrigen ausgeschaltet werden müssen. Wir sehen uns das am Beispiel der Optionsschaltflächen zur Auswahl der Art der Ausführung eines Auftrags an:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on radion button 1 - order execution type
      if(sparam=="ActP_Exe1_radio2")
      {
         //check the radio button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //set the appropriate state
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the other radio buttons
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            //redraw the chart
            ChartRedraw();
            //finish the execution of function
            return;
         }
         //redraw the chart
         ChartRedraw();
         //finish the execution of function
         return;
      }
 
       //Similarly for the radio button 2
      if(sparam=="ActP_Exe2_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }   

       //Similarly for the radio button 3
      if(sparam=="ActP_Exe3_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

Die Optionsschaltflächen für die Art der Ablaufzeit unterscheiden sich nur dadurch, dass man bei Anklicken der dritten eine weitere Einstellung vornehmen muss und zwar die Eingabe eines neuen Datums in das Eingabefeld für die Ablaufzeit des Auftrags:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the 3rd radio button - order expiration date
      if(sparam=="ActP_exp3_radio2")
      {
         //checking it state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the remained radio buttons
            ObjectSetInteger(0, "ActP_exp1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE, false);
            //set the new date to the date edit field
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, White);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, false);
            ObjectSetString(0, "ActP_exp_edit2", OBJPROP_TEXT, TimeToString(time_current));
            //if auxiliary lines are allowed 
            //set the new time line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, time_current);
            ChartRedraw();
            return;
         }

          //if it isn't selected
         else
         {
            //set the edit field as not available for editing
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, true);
            //remove the auxiliary line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, 0);
         }      
         ChartRedraw();
         return;
   ...   
   }
...
}

Damit wären auch die Optionsschaltflächen erledigt.

5,4. Anlegen und Verarbeiten von Ereignissen für die aufklappenden Listen

Wir wählen den zu ändernden/schließenden/löschenden Auftrag/Vorgang sowie die Farben für das Bedienfeld in aufklappenden Listen aus. Wir fangen mit der Liste der Handelsvorgänge/Aufträge an.

Das Erste, was uns unter der Registerkarte „Ändern/Schließen“ begegnet, ist eine Schaltfläche mit der Aufschrift „Auftrag auswählen -->“, sie dient uns auch als Schaltfläche zum Aufrufen unserer Liste. Durch Betätigung dieser Schaltfläche klappt sich die Liste auf, und, nachdem wir unsere Auswahl vorgenommen haben, verschwindet sie wieder.  Sehen wir uns die Ereignisverarbeitungsroutine CHARTEVENT_OBJECT_CLICK dieser Schaltfläche etwas genauer an:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the drop-down list activate button (order select)

      if(sparam=="ActP_ord_button5")
      {
         //check status
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //the list is activated
         if(selected)// the list is selected
         {
            //delete interface
            DeleteScheme("ActP", true);
            //arrays for serving the information about the orders
            string info[100];
            //array for the tickets
            int tickets[100];
            //initialize it
            ArrayInitialize(tickets, -1);
            //get orders info
            get_ord_info(info, tickets);
            //create the list
            create_list(info, tickets);
         }
          //the list isn't active
         else
         {
            //delete it
            DeleteLists("ActP_orders_list_");
         }
          //redraw the chart
         ChartRedraw();
          //finish the function
         return;
      }      
   ...   
   }
...
}

Unsere vordringlichste Aufgabe besteht darin festzustellen, ob auf dem Markt Vorgänge/Aufträge vorhanden sind und, falls ja, ihnen die für die Abbildung in der Liste erforderlichen Informationen zu entnehmen. Das besorgt die Funktion get_ord_info():

//+------------------------------------------------------------------+
//| The function for obtaining the information about orders          |
//+------------------------------------------------------------------+
void get_ord_info(string &info[],int &tickets[])
  {
   //initialize the counter
   int cnt=0;
   string inf;
   //if there is an open position
   if(PositionSelect(Symbol()))
     {
     //combine all order infomation in a single line
      double vol=PositionGetDouble(POSITION_VOLUME);
      int typ=PositionGetInteger(POSITION_TYPE);
      if(typ==POSITION_TYPE_BUY) inf+="BUY ";
      if(typ==POSITION_TYPE_SELL) inf+="SELL ";
      inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
      inf+=" at "+DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), Digits());
      //write the results
      info[cnt]=inf;
      tickets[cnt]=0;
      //increment the counter
      cnt++;
     }

   //all orders
   for(int i=0;i<OrdersTotal();i++)
     {
      //get ticket
      int ticket=OrderGetTicket(i);
      //if order symbol is equal to chart symbol
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         //combine all order infomation in a single line
         inf="#"+IntegerToString(ticket)+" ";
         int typ=OrderGetInteger(ORDER_TYPE);
         double vol=OrderGetDouble(ORDER_VOLUME_CURRENT);
         if(typ==ORDER_TYPE_BUY_LIMIT) inf+="BUY LIMIT ";
         if(typ==ORDER_TYPE_SELL_LIMIT) inf+="SELL LIMIT ";
         if(typ==ORDER_TYPE_BUY_STOP) inf+="BUY STOP ";
         if(typ==ORDER_TYPE_SELL_STOP) inf+="SELL STOP ";
         if(typ==ORDER_TYPE_BUY_STOP_LIMIT) inf+="BUY STOP LIMIT ";
         if(typ==ORDER_TYPE_SELL_STOP_LIMIT) inf+="SELL STOP LIMIT ";
         inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
         inf+=" at "+DoubleToString(OrderGetDouble(ORDER_PRICE_OPEN), Digits());
         //write the results
         info[cnt]=inf;
         tickets[cnt]=ticket;
         //increment the counter
         cnt++;
        }
     }
  }

Sie speichert die Informationen und Händlerzettel der Aufträge und Handelsvorgänge in Datenfeldern (Arrays).

Anschließend legt die Funktion create_list() auf der Grundlage dieser Informationen eine Liste an:

//+------------------------------------------------------------------+
//| The function creates list of positions                           |
//| info - array for the positions                                   |
//| tickets - array for the tickets                                  |
//+------------------------------------------------------------------+
void create_list(string &info[],int &tickets[])
  {
   //get the coordinates of the list activation button
   int x=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YDISTANCE)+ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YSIZE);
   //get colors
   color col=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_COLOR);
   color bgcol=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_BGCOLOR);
   //get window height
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   int y_cnt=0;
   //proceed arrays
   for(int i=0;i<100;i++)
     {
      //break if end reached
      if(tickets[i]==-1) break;
      //calculate the list item coordinates
      int y_pos=y+y_cnt*20;
      //if the windiow limits are reachedl, start a new column
      if(y_pos+20>wnd_height) {x+=300; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      string name="ActP_orders_list_"+IntegerToString(i)+" $"+IntegerToString(tickets[i]);
      //create element
      create_button(name,info[i],x,y_pos,300,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

Und schließlich löscht die Funktion DeleteLists() alle Elemente der Liste:

//+------------------------------------------------------------------+
//| The function for the list deletion                               |
//+------------------------------------------------------------------+
void  DeleteLists(string IDstr)
  {
   //proceed all objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //delete lists
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

Jetzt erfolgt also bei Betätigung der Aktivierungsschaltfläche die Erstellung einer Liste. Wir werden sie zwingen, etwas zu tun, denn beim Anklicken der Listenelemente sollen ja bestimmte Vorgänge ausgeführt werden. Und zwar: das Laden der Benutzeroberfläche für die Bearbeitung eines Auftrags sowie deren Füllung mit den entsprechenden Angaben. 

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   // Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click not on an item of order selection list
      if(StringFind(sparam, "ActP_orders_list_")<0)
      {
          //Remove it
         DeleteLists("ActP_orders_list_");
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //Click on the order selection list item
      else
      {
          //Set a new name for the activation button
         ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, ObjectGetString(0, sparam, OBJPROP_TEXT));
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //get ticket from the list item description
         int ticket=StringToInteger(StringSubstr(sparam, StringFind(sparam, "$")+1));
          //Load the interface
         SetScheme(ticket);
          //and delete the list
         DeleteLists("ActP_orders_list_");
          //chart redraw
         ChartRedraw();
      }
   ...   
   }
...
}

Hier müssen wir clever sein. Weil wir vorher weder den Umfang der Liste noch die Bezeichnungen ihrer Objekte kennen, müssen wir diese Informationen zunächst ermitteln, indem wir die Bezeichnungen der Listenelemente untersuchen. Die Funktion SetScheme() richtet die benötigte Benutzeroberfläche ein, entweder zur Verarbeitung eines Geschäftsvorgangs oder eines bedingten Auftrags, einer Pending Order:

//+------------------------------------------------------------------+
//| The function sets the interface depending on type:               |
//| position or pending order                                        |
//| t - ticket                                                       |
//+------------------------------------------------------------------+
void SetScheme(int t)
  {
   //if position
   if(t==0)
     {
      //check for its presence
      if(PositionSelect(Symbol()))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(6);
         //set position parameters
         SetPositionParams();
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
   //if order
   if(t>0)
     {
      //check for its presence
      if(OrderSelect(t))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(7);
         //set order parameters
         SetOrderParams(t);
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
  }

Die Funktionen SetPositionParams() und SetOrderParams() übernehmen die Einstellung der erforderlichen Eigenschaften der geladenen Benutzeroberfläche:

//+------------------------------------------------------------------+
//| Set position parameters for the objects                          |
//+------------------------------------------------------------------+
void SetPositionParams()
  {
   //if position is exists
   if(PositionSelect(Symbol()))
     {
      //get its parameters
      double pr=PositionGetDouble(POSITION_PRICE_OPEN);
      double lots=PositionGetDouble(POSITION_VOLUME);
      double sl=PositionGetDouble(POSITION_SL);
      double tp=PositionGetDouble(POSITION_TP);
      double mag=PositionGetInteger(POSITION_MAGIC);
      //and set new values to the objects
      ObjectSetString(0,"ActP_Pr_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit4",OBJPROP_TEXT,IntegerToString(mag));
      //redraw chart
      ChartRedraw();
     }
   //if there isn't position, show message 
   else MessageBox("There isn't open position for "+Symbol());
  }
//+------------------------------------------------------------------+
//| Set pending order parameters for the objects                     |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
void SetOrderParams(int ticket)
  {
   //if order exists
   if(OrderSelect(ticket) && OrderGetString(ORDER_SYMBOL)==Symbol())
     {
      //get its parameters
      double pr=OrderGetDouble(ORDER_PRICE_OPEN);
      double lots=OrderGetDouble(ORDER_VOLUME_CURRENT);
      double sl=OrderGetDouble(ORDER_SL);
      double tp=OrderGetDouble(ORDER_TP);
      double mag=OrderGetInteger(ORDER_MAGIC);
      double lim=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
      datetime expir=OrderGetInteger(ORDER_TIME_EXPIRATION);
      ENUM_ORDER_TYPE type=OrderGetInteger(ORDER_TYPE);
      ENUM_ORDER_TYPE_TIME expir_type=OrderGetInteger(ORDER_TYPE_TIME);
      
      //of order type is stoplimit, modify the interface
      if(type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT)
        {
         //set new value to the order price edit
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,DoubleToString(lim,_Digits));
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,White);
         //set order price available for edit
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,false);
        }
      //if order type isn't stoplimit, modify the interface
      else
        {
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,"");
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,LavenderBlush);
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,true);
        }

      //check expiration type
      //and set interface elements
      switch(expir_type)
        {
         case ORDER_TIME_GTC:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_DAY:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_SPECIFIED:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,1);
            //in addition, set new value to the edit
            ObjectSetString(0,"ActP_exp_edit3",OBJPROP_TEXT,TimeToString(expir));
            break;
           }
        }
      //set new values for the objects
      ObjectSetString(0,"ActP_Pr_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      ObjectSetString(0,"ActP_ticket_edit3",OBJPROP_TEXT,IntegerToString(ticket));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit3",OBJPROP_TEXT,IntegerToString(mag));
      ChartRedraw();
     }
   //if there isn't such order, show message
   else MessageBox("There isn't an order with ticket "+IntegerToString(ticket)+" for "+Symbol());
  }

Und zu guter Letzt müssen die Listen beim Anklicken des Diagramms gelöscht werden, dazu dient das Ereignis CHARTEVENT_CLICK:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - is click on the chart
   if(id==CHARTEVENT_CLICK)
   {
       //delete all lists
      DeleteLists("ActP_orders_list_");
      DeleteLists("ActP_color_list_"); 
       //Set the activate buttons to the unpressed state
      ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
      ChartRedraw(); 
      return;
   }
...
}

Im Ergebnis erhalten wir diese sympathisch aussehende Liste:

Abbildung 15. Beispiel einer aufklappenden Liste im Bedienfeld „Ändern/Schließen“

Abbildung15. Beispiel einer aufklappenden Liste im Bedienfeld „Ändern/Schließen“

Bleibt noch die Erstellung einer Liste zur Auswahl der Farbe unter der Registerkarte „Einstellungen“. 

Sehen wir uns die Verarbeitungsroutinen der Aktivierungsschaltflächen an:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the button to activate the colors drop-down list
      if(sparam=="ActP_col1_button6")
      {
          //check state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //the list is active
         if(selected)//the list is active
         {
             //creat list
            create_color_list(100, "ActP_col1_button6", 1);
             //Set the position of the remaining buttons to "unpressed"
            ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
            ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
             //delete other lists
            DeleteLists("ActP_color_list_2");
            DeleteLists("ActP_color_list_3");                 
         }
          //the list isn't selected
         else
         {
             //delete it
            DeleteLists("ActP_color_list_");
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }
   ...   
   }
...
}

Wir gehen hierbei ganz ähnlich vor wie bei der Liste zur Auswahl des Auftrags.

Die Funktion zur Erstellung der Liste unterscheidet sich etwas:

//+------------------------------------------------------------------+
//| Function for creating the colors list                            |
//| y_max - maximal list widthа                                      |
//| ID - list ID                                                     |
//| num - interface number                                           |
//+------------------------------------------------------------------+
void create_color_list(int y_max,string ID,int num)
  {
  //Get the coordinates of the list activation button
   int x=ObjectGetInteger(0,ID,OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, ID, OBJPROP_YDISTANCE)+ObjectGetInteger(0, ID, OBJPROP_YSIZE);
   //get color
   color col=ObjectGetInteger(0,ID,OBJPROP_COLOR);
   //and window width
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   y_max+=y;
   int y_cnt=0;
   //We will go through the colors array
   for(int i=0;i<132;i++)
     {
      color bgcol=colors[i];
      //calculate list item coordinates
      int y_pos=y+y_cnt*20;
      //if we reached the boundaries of the window, start new column
      if(y_pos+20>wnd_height || y_pos+20>y_max) {x+=20; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      //create new element
      string name="ActP_color_list_"+IntegerToString(num)+ID+IntegerToString(i);
      create_button(name,"",x,y_pos,20,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

Außerdem bearbeiten wir das Anklicken eines Listenelements:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click isn't on the color list button
      if(StringFind(sparam, "ActP_color_list_1")<0)
      {
          //delete list
         DeleteLists("ActP_color_list_1");
          //set color list activation button to "unpressed"
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //click on the color list
      else
      {
          //get color from the list
         color col=ObjectGetInteger(0, sparam, OBJPROP_BGCOLOR);
          //set it for all the buttons
         SetButtonsColor(col);
          //set button to unpressed
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //delete list
         DeleteLists("ActP_color_list_1");
          //redraw chart
         ChartRedraw();
      }
   ...   
   }
...
}

Die Funktion SetButtonsColor() legt die Farbe der Schaltflächen fest:

//+------------------------------------------------------------------+
//| The function sets color for all buttons                          |
//| col - color                                                      |
//+------------------------------------------------------------------+
void SetButtonsColor(color col)
  {
   //We will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel and its has a button type
      //set color
      if(StringFind(n,"ActP")>=0 && ObjectGetInteger(0,n,OBJPROP_TYPE)==OBJ_BUTTON) 
         ObjectSetInteger(0,n,OBJPROP_BGCOLOR,col); 
     }
   //set global variable
   GlobalVariableSet("ActP_buttons_color",col);
  }

Werten wir das Ergebnis aus:

Abbildung 16. Festlegung der Schaltflächenfarbe

Abbildung16. Festlegung der Schaltflächenfarbe

Die Listen zur Auswahl der Farbe für die Beschriftung und der Schriftfarbe funktionieren ähnlich. Im Ergebnis lässt sich das Bedienfeld mit ein paar Mausklicks ganz leicht einfärben:

Abbildung 17. Geänderte Farben des Bedienfelds, der Schaltflächen und der Schrift

Abbildung 17. Geänderte Farben des Bedienfelds, der Schaltflächen und der Schrift

Jetzt haben wir auch die Listen erledigt. Machen wir also mit den Eingabefeldern weiter.

5,5. Die Verarbeitung von Eingabefeldereignissen

Eingabefelder erzeugen ein CHARTEVENT_OBJECT_ENDEDIT-Ereignis, das bei Abschluss der Bearbeitung des Texts in dem Feld erscheint. Der einzige Grund, aus dem wir dieses Ereignis verarbeiten müssen, ist die Einrichtung der Hilfslinien für die Kurse, die denen der Eingabefelder entsprechen.

Sehen wir uns als Beispiel die Festlegung einer Stoplinie an:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //End edit event
   if(id==CHARTEVENT_OBJECT_ENDEDIT)//end edit event
   {
   ...
      //if edit field is SL field
      if(sparam=="ActP_SL_edit1")
      {
        //and auxiliary lines are enabled
         if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
         {
            //get text from the field
            double sl_val=StringToDouble(ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT));
            //move lines at new position
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, sl_val);
         }
         //redraw chart
         ChartRedraw();
         //it ins't necessary to proceed the other objects, because the event from the one
         return;
      }
   ...   
   }
...
}

Die Bearbeitung der übrigen Eingabefelder verläuft ähnlich.

5.6 Die Verarbeitung von Zeitgeberereignissen

Der Zeitgeber (Timer) dient zur Überwachung der Hilfslinien, damit bei einer Verschiebung der Linien die Werte der Kurse, für die sie festgelegt wurden, automatisch in das Eingabefeld übertragen werden. Bei jeder Änderung des Zeitgebers wird die Funktion OnTimer() ausgeführt.

Wir sehen uns als Beispiel die Einrichtung der Überwachung der Stop Loss- und Take Profit-Linien unter der Registerkarte „Börse“ an:

void OnTimer()// Timer handler
{
   //panel 1 is active
   if(ObjectGetInteger(0, "ActP_main_1", OBJPROP_STATE)==1)
   {  
      //if auxiliary lines are allowed
      if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
      {
         //set new values to the edit fields
         double sl_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_SL_line1", OBJPROP_PRICE), _Digits);
         //stop loss
         ObjectSetString(0, "ActP_SL_edit1", OBJPROP_TEXT, DoubleToString(sl_pr, _Digits));
         //take profit
         double tp_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_TP_line1", OBJPROP_PRICE), _Digits);
         ObjectSetString(0, "ActP_TP_edit1", OBJPROP_TEXT, DoubleToString(tp_pr, _Digits));
      }   
   }
   ...
   //redraw chart
   ChartRedraw();
}
//+------------------------------------------------------------------+

Die Überwachung der anderen Linien wird ähnlich eingerichtet.


6. Ausführung von Handelsoperationen

So, alle erforderlichen Eingabefelder sind ausgefüllt und die Kontrollkästchen, Linien und Optionsschaltflächen sind angelegt. Jetzt ist es höchste Zeit, auf der Grundlage dieser Daten zu handeln.

6,1. Die Eröffnung eines Handelsvorgangs

Unter der Registerkarte „Börse“ haben wir die Schaltflächen „Buy“ (Kaufen) und „Sell“ (Verkaufen). Wenn alle Felder korrekt ausgefüllt sind, wird der Abschluss beim Anklicken der entsprechenden Schaltfläche ausgeführt.

Werfen wir einen Blick auf die Verarbeitungsroutinen dieser Schaltflächen:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the Buy button
      if(sparam=="ActP_buy_button1")
      {
          //check its state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it "pressed"
         if(selected)
         {
             //try to perform a deal
            deal(ORDER_TYPE_BUY);
             //and set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }

          //redraw chart
         ChartRedraw();
          //and finish the function execution
         return;
      }
      //******************************************
       //the similar for the sell button
      if(sparam=="ActP_sell_button1")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            deal(ORDER_TYPE_SELL);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

Wie wir sehen, wird die Funktion deal() ausgeführt:

//+------------------------------------------------------------------+
//| Deal function                                                    |
//+------------------------------------------------------------------+
int deal(ENUM_ORDER_TYPE typ)
  {
   //get the data from the objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit1",OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit1",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_Magic_edit1", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0, "ActP_Dev_edit1", OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit1",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0,"ActP_Exe2_radio1",OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=Ask;
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.deviation=dev;
   req.type=typ;
   req.type_filling=filling;
   req.magic=mag;
   req.comment=comm;
   //send order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Da ist nichts Übersinnliches. Wir lesen zunächst die erforderlichen Daten aus den Objekten aus und erstellen auf ihrer Grundlage eine Handelsanfrage.

Schauen wir uns an, was wir getan haben:

Abbildung 18. Handelsoperationen - Ergebnis des Abschlusses eines Kaufvorgangs

Abbildung 18. Handelsoperationen - Ergebnis des Abschlusses eines Kaufvorgangs

Und siehe da, der Kaufvorgang wurde erfolgreich abgeschlossen.

6,2. Platzieren einer Pending Order

Mit den Schaltflächen „Buy“ und „Sell“ der Registerkarte „Pending“ lassen sich bedingte Aufträge (Pending Orders) platzieren.

Wir sehen uns die Verarbeitungsroutinen an:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the pending order set button
      if(sparam=="ActP_buy_button2")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //if it pressed
         if(selected)
         {
            ENUM_ORDER_TYPE typ; 
            //get the pending order from the edit
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            //if it isn't stoplimit order
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               //if the order price is below the current price, set limit order
               if(Ask>pr) typ=ORDER_TYPE_BUY_LIMIT; 
               //overwise - stop order
               else typ=ORDER_TYPE_BUY_STOP;
            }
              //if stoplimit order is specified
            else
            {
               //set operation type
               typ=ORDER_TYPE_BUY_STOP_LIMIT;
            }   
              //try to place order
            order(typ);
              //set button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
      //******************************************  

       //similar for the sell pending order
      if(sparam=="ActP_sell_button2")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            ENUM_ORDER_TYPE typ;
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               if(Bid<pr) typ=ORDER_TYPE_SELL_LIMIT;
               else typ=ORDER_TYPE_SELL_STOP;
            }
            else
            {
               typ=ORDER_TYPE_SELL_STOP_LIMIT;
            }   
            order(typ);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }        
   ...   
   }
...
}

Hier erfolgt die Bestimmung der Art des künftigen Auftrages aufgrund der Lage des aktuellen Kurses relativ zu dem festgelegten Kurs, nach dessen Erreichen die Funktion order() den Auftrag platziert:

//+------------------------------------------------------------------+
//| The function places an order                                     |
//+------------------------------------------------------------------+
int order(ENUM_ORDER_TYPE typ)
  {
   //get the order details from the objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit2",OBJPROP_TEXT));
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit2",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit2", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit2", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit2",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit2",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0,"ActP_Magic_edit2",OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit2",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   if(ObjectGetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_RETURN;
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res; 
   req.action=TRADE_ACTION_PENDING;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type=typ;
   req.type_filling=filling;
   req.type_time=expir_type;
   req.expiration=expir;
   req.comment=comm;
   req.magic=mag;
   //place order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Schauen wir uns an, was wir getan haben:

Abbildung 19. Handelsoperationen - Ergebnis der Platzierung einer Pending Order

Abbildung 19. Handelsoperationen - Ergebnis der Platzierung einer Pending Order

Der Auftrag mit Buy Stoplimit wurde erfolgreich platziert.

6,3. Ändern eines Vorgangs

Die Schaltfläche „Ändern“ unter der Registerkarte „Ändern/Schließen“ dient zur Vornahme der Änderungen an dem ausgewählten Vorgang:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the modify position button
      if(sparam=="ActP_mod_button4")
      {
          //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it pressed
         if(selected)//if pressed
         {
            //modify position
            modify_pos();
            //delete the elements of the scheme
            DeleteScheme("ActP" ,true);
            //and reset it (update the interface)
            SetScheme(0);
            //set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

Unmittelbar verantwortlich für die Änderung ist die Funktion modify_pos():

//+------------------------------------------------------------------+
//| The function modifies the position parameters                    |
//+------------------------------------------------------------------+
int modify_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the details from the edit objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit4",OBJPROP_TEXT));  
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit4", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0,"ActP_dev_edit4",OBJPROP_TEXT));
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;  
   req.action=TRADE_ACTION_SLTP;
   req.symbol=Symbol();
   req.sl=NormalizeDouble(SL, _Digits);
   req.tp=NormalizeDouble(TP, _Digits);
   req.deviation=dev;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Ergebnis:

Abbildung 20. Handelsoperationen - Ergebnis der Änderung der Eigenschaften eines Handelsvorgangs (Festlegen der Take Profit- und Stop Loss-Grenzen)

Abbildung20. Handelsoperationen - Ergebnis der Änderung der Eigenschaften eines Handelsvorgangs (Festlegen der Take Profit- und Stop Loss-Grenzen)


Die Stop Loss- und Take Profit-Grenzen wurden erfolgreich geändert.

6,4. Schließen eines Vorgangs

Die Schaltfläche „Schließen“ unter der Registerkarte „Ändern/Schließen“ dient zum (möglicherweise teilweisen) Schließen dieses Vorgangs:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the close button
      if(sparam=="ActP_del_button4")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //try to close position
            int retcode=close_pos();
            //if successful
            if(retcode==10009)
            {
               //delete scheme elements
               DeleteScheme("ActP" ,true);
               //set the new text for the list activisation
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select order -->");
            }
            //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

Die Funktion close_pos() ist für die Änderung zuständig:

//+------------------------------------------------------------------+
//| Closes the position                                              |
//+------------------------------------------------------------------+
int close_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the position details from the objects
   double lots=StringToDouble(ObjectGetString(0,"ActP_lots_edit4",OBJPROP_TEXT));
   if(lots>PositionGetDouble(POSITION_VOLUME)) lots=PositionGetDouble(POSITION_VOLUME);
   int dev=StringToInteger(ObjectGetString(0, "ActP_dev_edit4", OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_mag_edit4", OBJPROP_TEXT));

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;

   //the opposite deal is dependent on position type
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {
      req.price=Bid;
      req.type=ORDER_TYPE_SELL;
     }
   else
     {
      req.price=Ask;
      req.type=ORDER_TYPE_BUY;
     }

   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.sl=0;
   req.tp=0;
   req.deviation=dev;
   req.type_filling=ORDER_FILLING_FOK;
   req.magic=mag;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Das Ergebnis: geschlossen wurden 1,5 von drei Posten des ausgewählten Geschäftsvorgangs:

Abbildung 21. Handelsoperationen - Teilschließung einer Position

Abbildung21. Handelsoperationen - Teilschließung einer Position

6,5. Änderung einer „Pending Order“

Die Schaltfläche „Ändern“ unter der Registerkarte „Ändern/Schließen“ dient zur Vornahme der Änderungen an dem ausgewählten Auftrag:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the order modify button
      if(sparam=="ActP_mod_button3")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {     
            //get the order ticket from the edit
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //modifying an order
            modify_order(ticket);
            //update interface
            DeleteScheme("ActP" ,true);
            SetScheme(ticket);
            //set button to unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

Verantwortlich für die Änderung ist die Funktion modify_order():

//+------------------------------------------------------------------+
//| The function modifies an order                                   |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int modify_order(int ticket)
  {
   //get the order details from the corresponding chart objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit3",OBJPROP_TEXT)); 
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit3",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit3", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit3", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit3",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit3",OBJPROP_TEXT));
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request to modify
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_MODIFY;
   req.order=ticket;
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type_time=expir_type;
   req.expiration=expir;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Hier sehen wir das Ergebnis - der Auftrag wurde erfolgreich geändert:

Abbildung 22. Änderung einer „Pending Order“

Abbildung 21. Änderung einer „Pending Order“

6,6. Das Löschen einer Pending Order

Die Schaltfläche „Löschen“ unter der Registerkarte „Ändern/Schließen“ dient zum Löschen des ausgewählten Auftrags:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the order delete button
      if(sparam=="ActP_del_button3")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //get the ticket from the list
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //try to delete order
            int retcode=del_order(ticket);
            //if successful
            if(retcode==10009)
            {
               //delete all objects of the scheme
               DeleteScheme("ActP" ,true);
               //set new text for the list activation button
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select an order -->");
            }
             //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

Die Funktion del_pos() ist verantwortlich für die Löschung des Auftrags:

//+------------------------------------------------------------------+
//| The function for pending order deletion                          |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int del_order(int ticket)
  {
   //prepare request for deletion
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_REMOVE;
   req.order=ticket;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Schauen wir uns das Ergebnis an - der Auftrag wurde gelöscht:

Abbildung 23. Handelsoperationen - Löschen einer Pending Order

Abb. 23 Handelsoperationen - Löschen einer Pending Order


Fazit

Es wurden alle Funktionen des Bedienfeldes geprüft und für gut befunden.

Ich hoffe, dass die Kenntnisse, die Ihnen die Lektüre dieses Artikels vermittelt hat, Ihnen bei der Entwicklung aktiver Bedienfelder als unersetzliche Helfer bei der Arbeit an der Börse nützlich sein werden. 

Zur Aufnahme der Arbeit mit einem Bedienfeld muss zunächst die gepackte Archivdatei in den Ordner mit den Programm entpackt, anschließend der Indikator AP auf das Diagramm angewendet und erst dann das Active Panel-Bedienfeld des Expert-Systems gestartet werden.