Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil V). Klassen und Kollektionen für Handelsereignisse, Nachrichten an das Programm senden

Artyom Trishkin | 26 Juni, 2019

Inhalt

Neuordnung der Bibliotheksstruktur

In den vorangegangenen Artikeln haben wir begonnen, eine große plattformübergreifende Bibliothek zu erstellen, die die Entwicklung von Programmen für MetaTrader 5 und MetaTrader 4 Plattformen vereinfacht. Im vierten Teil haben wir die Verfolgung von Handelsereignissen auf dem Konto getestet. In diesem Artikel werden wir Klassen für Handelsereignisse entwickeln und diese in der Kollektion der Ereignisse platzieren. Von dort aus werden sie an das Basisobjekt der Enginebibliothek und die Steuerelement des Chartprogramms.

Aber zuerst wollen wir den Boden für die Weiterentwicklung der Bibliotheksstruktur bereiten.

Da wir viele verschiedene Kollektionen haben werden, und jede Kollektion ihre eigenen Objekte hat, die nur dieser Kollektion eigen sind, erscheint es sinnvoll, Objekte für jede Kollektion in separaten Unterordnern zu speichern.

Um dies zu tun, erstellen wir die Verzeichnisse Orders und Events im Unterordner Objects des Bibliotheksstamm-Verzeichnisses DoEasy.
Verschieben Sie alle zuvor erstellten Klassen aus dem Ordner Objects in den Ordner Orders, während der Ordner Events die Klassen der Ereignisobjekte speichert, die wir in diesem Artikel entwickeln werden.
Verschieben Sie auch die Datei Select.mqh aus Collections in Services, da wir ihr noch eine weitere Serviceklasse hinzufügen werden. Die Klasse verfügt über Methoden für den schnellen Zugriff auf beliebige Eigenschaften von Objekten aus den bestehenden und zukünftigen Kollektionen, d.h. sie sollte sich im Ordner der Service-Klassen befinden.

Nach dem Verschieben der Datei der Klasse CSelect und dem Verschieben der Auftragsobjektklassen in das neue Verzeichnis ändern sich auch die relativen Adressen der für ihre Kompilierung benötigten Dateien. Deshalb gehen wir durch die Listen der verschobenen Klassen und ersetzen die Adressen der darin enthaltenen Dateien:

In der Datei Order.mqh ersetzen wir den Pfad für das Einbinden der Datei mit den der Servicefunktionen.

#include "..\Services\DELib.mqh" 

mit

#include "..\..\Services\DELib.mqh"

In der Datei HistoryCollection.mqh, ersetzen wir die Pfade

#include "Select.mqh"
#include "..\Objects\HistoryOrder.mqh"
#include "..\Objects\HistoryPending.mqh"
#include "..\Objects\HistoryDeal.mqh"

 mit

#include "..\Services\Select.mqh"
#include "..\Objects\Orders\HistoryOrder.mqh"
#include "..\Objects\Orders\HistoryPending.mqh"
#include "..\Objects\Orders\HistoryDeal.mqh"

In der Datei MarketCollection.mqh, ersetzen wir die Pfade

#include "Select.mqh"
#include "..\Objects\MarketOrder.mqh"
#include "..\Objects\MarketPending.mqh"
#include "..\Objects\MarketPosition.mqh"

mit

#include "..\Services\Select.mqh"
#include "..\Objects\Orders\MarketOrder.mqh"
#include "..\Objects\Orders\MarketPending.mqh"
#include "..\Objects\Orders\MarketPosition.mqh"

Jetzt sollte sich alles ohne Fehler kompilieren lassen.

Da die Anzahl der anstehenden Kollektionen enorm ist, wäre es gut, den Besitz der Liste der Kollektionen anhand von CArrayObj zu unterscheiden, um die Liste zu identifizieren. Jede Kollektion verfügt über eine Methode, die den Zeiger auf die vollständige Liste der Kollektionen zurückführt. Wenn es irgendwo eine Methode gibt, die eine bestimmte Liste einer bestimmten Kollektion empfängt, dann müssen wir innerhalb dieser Methode in der Lage sein, die Liste, die an die Methode übergeben wird, genau zu identifizieren, indem sie zu der einen oder anderen Kollektion gehört, um zu vermeiden, dass ein zusätzliches Flag übergeben wird, das den Typ der an die Methode übergebenen Liste anzeigt.

Glücklicherweise bietet die Standardbibliothek dafür bereits das notwendige Werkzeug in Form der virtuellen Methode Type(), die die Objekt-ID zurückgibt.
Beispielsweise ist für CObject die zurückgegebene ID 0, während für CArrayObj die ID 0x7778 ist. Da die Methode virtuell ist, können die Nachkommen der Klassen ihre eigenen Methoden verwenden, die bestimmte IDs zurückgeben.

Alle unsere Liste der Kollektionen basieren auf der Klasse CArrayObj. Wir erstellen unsere eigene Klasse CListObj , die ein Nachkomme der Klasse CArrayObj ist, und die Listen-ID wird in ihrer virtuellen Methode Type() zurückgegeben. Die ID selbst wird im Klassenkonstruktor als Konstante gesetzt. So werden wir weiterhin Zugriff auf unsere Kollektionen wie auf das Objekt CArrayObj erhalten, aber jetzt wird jede Liste ihre eigene spezifische ID haben.

Zuerst setzen wir die notwendigen IDs der Listen der Kollektionen in der Datei Definiert.mqh und fügen das Makro hinzu, das die Funktion mit der Fehlerzeilennummer beschreibt, um Debugging-Meldungen anzuzeigen, die eine Zeichenkette enthalten, von der diese Nachricht gesendet wird, um das Problem während des Debuggens im Code zu lokalisieren:

//+------------------------------------------------------------------+
//| Macro-Substitution                                               |
//+------------------------------------------------------------------+
//--- Beschreibung der Funktion mit der Zeilennummer des Fehlers
#define DFUN_ERR_LINE            (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                     (__FUNCTION__+": ")        // "Funktionsbeschreibung"
#define COUNTRY_LANG             ("Russian")                // Landessprache
#define END_TIME                 (D'31.12.3000 23:59:59')   // Enddatum der abgefragten Kontohistorie
#define TIMER_FREQUENCY          (16)                       // Minimalfrequenz des Timers der Bibliothek in Millisekunden
#define COLLECTION_PAUSE         (250)                      // Pause des Timers der Kollektion der Aufträge und Deals in Millisekunden
#define COLLECTION_COUNTER_STEP  (16)                       // Increment des Timerzählers der Kollektion der Aufträge und Deals
#define COLLECTION_COUNTER_ID    (1)                        // Timerzählers der Kollektion von Aufträgen und Deals
#define COLLECTION_HISTORY_ID    (0x7778+1)                 // ID der Historische Kollektionsliste
#define COLLECTION_MARKET_ID     (0x7778+2)                 // ID der Marktkollektionsliste
#define COLLECTION_EVENTS_ID     (0x7778+3)                 // ID der Ereigniskollektionsliste
//+------------------------------------------------------------------+

Erstellen wir nun die Klassse CListObj in der Datei ListObj.mqh im Ordner Collections. Die Basisklasse dafür ist CArrayObj:

//+------------------------------------------------------------------+
//|                                                      ListObj.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Klasse der Kollektionsliste                                      |
//+------------------------------------------------------------------+
class CListObj : public CArrayObj
  {
private:
   int               m_type;                    // Listentyp
public:
   void              Type(const int type)       { this.m_type=type;     }
   virtual int       Type(void)           const { return(this.m_type);  }
                     CListObj()                 { this.m_type=0x7778;   }
  };
//+------------------------------------------------------------------+

Alles, was wir hier tun müssen, ist, ein Mitglied der Klasse, die den Listentyp enthält, zu deklarieren, die Methode zur Definition des Listentyps und die Methode virtual zur Rückgabe hinzuzufügen.
Setzen wir im Klassenkonstruktor den Standardlistentyp gleich dem der Liste CArrajObj. Er kann dann von einem aufrufenden Programm aus mit der Methode Type() neu definiert werden.

Nun müssen wir alle Kollektionslisten von der Klasse erben, um jeder Liste eine eigene Such-ID zuordnen zu können. Diese ID ermöglicht es uns, den Besitz der Liste in allen Methoden zu verfolgen, an die die Liste übergeben wird.

Öffnen Sie die Datei HistoryCollection.mqh, biden Sie die Klasse CListObj und leiten Sie die Klasse CHistoryCollection von CListObj ab.

//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\HistoryOrder.mqh"
#include "..\Objects\Orders\HistoryPending.mqh"
#include "..\Objects\Orders\HistoryDeal.mqh"
//+------------------------------------------------------------------+
//| Kollektion der historischen Aufträge und Deals                   |
//+------------------------------------------------------------------+
class CHistoryCollection : public CListObj
  {

Im Klassenkonstruktor definieren den historischen Kollektionslistentyp, den wir als COLLECTION_HISTORY_ID in der Datei Defines.mqh definiert haben:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CHistoryCollection::CHistoryCollection(void) : m_index_deal(0),m_delta_deal(0),m_index_order(0),m_delta_order(0),m_is_trade_event(false)
  {
   this.m_list_all_orders.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN #else SORT_BY_ORDER_TIME_CLOSE #endif );
   this.m_list_all_orders.Clear();
   this.m_list_all_orders.Type(COLLECTION_HISTORY_ID);
  }
//+------------------------------------------------------------------+

Wir machen das gleiche mit der Klasse CMarketCollection in der Datei MarketCollection.mqh:

//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\MarketOrder.mqh"
#include "..\Objects\Orders\MarketPending.mqh"
#include "..\Objects\Orders\MarketPosition.mqh"
//+------------------------------------------------------------------+
//| Kollektion der Marktorders und Positionen                        |
//+------------------------------------------------------------------+
class CMarketCollection : public CListObj
  {

Definieren Sie im Klassenkonstruktor den Kollektionstyp für Market, die wir in der Datei Defines.mqh als COLLECTION_MARKET_ID angegeben haben:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CMarketCollection::CMarketCollection(void) : m_is_trade_event(false),m_is_change_volume(false),m_change_volume_value(0)
  {
   this.m_list_all_orders.Sort(SORT_BY_ORDER_TIME_OPEN);
   this.m_list_all_orders.Clear();
   ::ZeroMemory(this.m_struct_prev_market);
   this.m_struct_prev_market.hash_sum_acc=WRONG_VALUE;
   this.m_list_all_orders.Type(COLLECTION_MARKET_ID);
  }
//+------------------------------------------------------------------+

Nun hat jede der Kollektionslisten ihre ID, was die Identifizierung von Listen nach ihrem Typ vereinfacht.



Da wir neue Kollektionen für die Arbeit mit neuen Datentypen hinzufügen sollen (einschließlich der Kollektion von Kontoereignissen im vorliegenden Artikel), werden wir die neuen Enumeration verwenden. Um Namenskonflikte zu vermeiden, müssen wir die Namen einiger zuvor erstellter Makro-Substitutionen ersetzen:

//+------------------------------------------------------------------+
//| Mögliche Sortierkriterien von Aufträgen und Deals                |
//+------------------------------------------------------------------+
#define FIRST_ORD_DBL_PROP          (ORDER_PROP_INTEGER_TOTAL)
#define FIRST_ORD_STR_PROP          (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL)
enum ENUM_SORT_ORDERS_MODE
  {
   //--- Sortieren nach den Integer-Eigenschaften
   SORT_BY_ORDER_TICKET          =  0,                      // Sortieren nach Auftrags-Ticket
   SORT_BY_ORDER_MAGIC           =  1,                      // Sortieren nach der Magicnummer
   SORT_BY_ORDER_TIME_OPEN       =  2,                      // Sortieren nach Eröffnungszeit
   SORT_BY_ORDER_TIME_CLOSE      =  3,                      // Sortieren nach Schlusszeit
   SORT_BY_ORDER_TIME_OPEN_MSC   =  4,                      // Sortieren nach Eröffnungszeit des Auftrags in Millisekunden
   SORT_BY_ORDER_TIME_CLOSE_MSC  =  5,                      // Sortieren nach der Schlusszeit des Auftrags in Millisekunden
   SORT_BY_ORDER_TIME_EXP        =  6,                      // Sortieren nach der Verfallszeit
   SORT_BY_ORDER_STATUS          =  7,                      // Sortieren nach dem Auftragsstatus (Marktorder/Pending-Order/Deal/Saldo, Gutschriften)
   SORT_BY_ORDER_TYPE            =  8,                      // Sortieren nach dem Auftragstyp
   SORT_BY_ORDER_REASON          =  10,                     // Sortieren nach Auftrag/Deal/Position/Quelle
   SORT_BY_ORDER_STATE           =  11,                     // Sortieren nach dem Auftragsstatus
   SORT_BY_ORDER_POSITION_ID     =  12,                     // Sortieren nach der Positions-ID
   SORT_BY_ORDER_POSITION_BY_ID  =  13,                     // Sortieren nach der ID der entgegengesetzten Position
   SORT_BY_ORDER_DEAL_ORDER      =  14,                     // Sortieren nach dem Auftrag als Basis des Deals
   SORT_BY_ORDER_DEAL_ENTRY      =  15,                     // Sortieren nach der Richtung der Deals – IN, OUT or IN/OUT
   SORT_BY_ORDER_TIME_UPDATE     =  16,                     // Sortieren nach der Änderungszeit der Position in Sekunden
   SORT_BY_ORDER_TIME_UPDATE_MSC =  17,                     // Sortieren nach der Änderungszeit der Position in Millisekunden
   SORT_BY_ORDER_TICKET_FROM     =  18,                     // Sortieren nach dem Tickets der Ober-Order
   SORT_BY_ORDER_TICKET_TO       =  19,                     // Sortieren nach der Ticket der abgeleiteten Order
   SORT_BY_ORDER_PROFIT_PT       =  20,                     // Sortieren nach dem Auftragsgewinn in Points
   SORT_BY_ORDER_CLOSE_BY_SL     =  21,                     // Sortieren nach dem Flag für das Schließen durch StopLoss
   SORT_BY_ORDER_CLOSE_BY_TP     =  22,                     // Sortieren nach dem Flag für das Schließen durch TakeProfit
   //--- Sortieren nach den Double-Eigenschaften
   SORT_BY_ORDER_PRICE_OPEN      =  FIRST_ORD_DBL_PROP,     // Sortieren nach dem Eröffnungspreis
   SORT_BY_ORDER_PRICE_CLOSE     =  FIRST_ORD_DBL_PROP+1,   // Sortieren nach Schließpreis
   SORT_BY_ORDER_SL              =  FIRST_ORD_DBL_PROP+2,   // Sortieren nach StopLoss-Preis
   SORT_BY_ORDER_TP              =  FIRST_ORD_DBL_PROP+3,   // Sortieren nach TakeProfit-Preis
   SORT_BY_ORDER_PROFIT          =  FIRST_ORD_DBL_PROP+4,   // Sortieren nach Gewinn
   SORT_BY_ORDER_COMMISSION      =  FIRST_ORD_DBL_PROP+5,   // Sortieren nach Kommission
   SORT_BY_ORDER_SWAP            =  FIRST_ORD_DBL_PROP+6,   // Sortieren nach Swap
   SORT_BY_ORDER_VOLUME          =  FIRST_ORD_DBL_PROP+7,   // Sortieren nach Volumen
   SORT_BY_ORDER_VOLUME_CURRENT  =  FIRST_ORD_DBL_PROP+8,   // Sortieren nach nicht ausgeführtem Volumen
   SORT_BY_ORDER_PROFIT_FULL     =  FIRST_ORD_DBL_PROP+9,   // Sortieren nach Gewinn+Kommission+Swap
   SORT_BY_ORDER_PRICE_STOP_LIMIT=  FIRST_ORD_DBL_PROP+10,  // Sortieren nach Limit-Order wenn StopLimit-Order aktiviert ist
   //--- Sortieren nach den String-Eigenschaften
   SORT_BY_ORDER_SYMBOL          =  FIRST_ORD_STR_PROP,     // Sortieren nach dem Symbol
   SORT_BY_ORDER_COMMENT         =  FIRST_ORD_STR_PROP+1,   // Sortieren nach dem Kommentar
   SORT_BY_ORDER_EXT_ID          =  FIRST_ORD_STR_PROP+2    // Sortieren nach Auftrags-ID eines externen Handelssystems
  };
//+------------------------------------------------------------------+

Da wir derzeit die Dateien Defines.mqh bearbeiten, fügen Sie alle notwendigen Enumerationen der Ereignisklassen und die Kollektion der Kontoereignisse hinzu:

//+------------------------------------------------------------------+
//| Ereignisstatus                                                   |
//+------------------------------------------------------------------+
enum ENUM_EVENT_STATUS
  {
   EVENT_STATUS_MARKET_POSITION,                            // Ereignis einer Marktposition (Eröffnen, teilweises Eröffnen, teilweises Schließen, Volumenerhöhung, Umkehrung)
   EVENT_STATUS_MARKET_PENDING,                             // Ereignis einer Pending-Order im Markt (Platzieren)
   EVENT_STATUS_HISTORY_PENDING,                            // Ereignis einer historische Pending-Order (Entfernen)
   EVENT_STATUS_HISTORY_POSITION,                           // Ereignis einer historischen Position (Schließen)
   EVENT_STATUS_BALANCE,                                    // Ereignis einer Saldenoperation (Gutschrift, Abbuchung von Geldern und Ereignisse vom Typ aus der Enumeration ENUM_DEAL_TYPE)
  };
//+------------------------------------------------------------------+
//| Ereignisgrund                                                    |
//+------------------------------------------------------------------+
enum ENUM_EVENT_REASON
  {
   EVENT_REASON_ACTIVATED_PENDING               =  0,       // Pending-Order Aktivierung
   EVENT_REASON_ACTIVATED_PENDING_PARTIALLY     =  1,       // Pending-Order teilweise Aktivierung
   EVENT_REASON_CANCEL                          =  2,       // Storniert
   EVENT_REASON_EXPIRED                         =  3,       // Verfallszeit des Auftrags
   EVENT_REASON_DONE                            =  4,       // Anfrage voll ausgeführt
   EVENT_REASON_DONE_PARTIALLY                  =  5,       // Anfrage teilweise ausgeführt
   EVENT_REASON_DONE_SL                         =  6,       // Schließen durch StopLoss
   EVENT_REASON_DONE_SL_PARTIALLY               =  7,       // Teilweise geschlossen durch StopLoss
   EVENT_REASON_DONE_TP                         =  8,       // Schließen durch TakeProfit
   EVENT_REASON_DONE_TP_PARTIALLY               =  9,       // Teilweise geschlossen durch TakeProfit
   EVENT_REASON_DONE_BY_POS                     =  10,      // Geschlossen durch entgegengesetzte Position
   EVENT_REASON_DONE_PARTIALLY_BY_POS           =  11,      // Teilweise geschlossen durch eine entgegengesetzte Position
   EVENT_REASON_DONE_BY_POS_PARTIALLY           =  12,      // Geschlossen durch eine Gegenposition mit Teilvolumen
   EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY =  13,      // Teilweises Schließen durch eine Gegenposition mit Teilvolumen
   //--- Konstanten bezüglich des Dealtyps DEAL_TYPE_BALANCE aus der Enumeration ENUM_DEAL_TYPE
   EVENT_REASON_BALANCE_REFILL                  =  14,      // Nachfüllen des Saldos
   EVENT_REASON_BALANCE_WITHDRAWAL              =  15,      // Gelder vom Konto abziehen
   //--- Liste der Konstanten bezüglich der Enumeration TRADE_EVENT_ACCOUNT_CREDIT aus der Enumeration ENUM_TRADE_EVENT und verschoben auf +13 relativ zu ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3)
   EVENT_REASON_ACCOUNT_CREDIT                  =  16,      // Kontogutschrift
   EVENT_REASON_ACCOUNT_CHARGE                  =  17,      // Zusätzliche Gebühren
   EVENT_REASON_ACCOUNT_CORRECTION              =  18,      // Korrekturbuchung
   EVENT_REASON_ACCOUNT_BONUS                   =  19,      // Bonusgutschrift
   EVENT_REASON_ACCOUNT_COMISSION               =  20,      // Zusätzliche Kommissionen
   EVENT_REASON_ACCOUNT_COMISSION_DAILY         =  21,      // Kommission zum Ende des Handelstages
   EVENT_REASON_ACCOUNT_COMISSION_MONTHLY       =  22,      // Kommission zum Ende des Monats
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY   =  23,      // Agenten-Kommission zum Ende des Handelstages
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY =  24,      // Agenten-Kommission zum Ende des Monats
   EVENT_REASON_ACCOUNT_INTEREST                =  25,      // Zinsgutschrift der freien Gelder
   EVENT_REASON_BUY_CANCELLED                   =  26,      // Storniert durch einen Kauf-Deal
   EVENT_REASON_SELL_CANCELLED                  =  27,      // Storniert durch einen Verkaufs-Deal
   EVENT_REASON_DIVIDENT                        =  28,      // Dividendengutsschrift
   EVENT_REASON_DIVIDENT_FRANKED                =  29,      // Gutschrift steuerbefreiter Dividenden
   EVENT_REASON_TAX                             =  30       // Steuern
  };
#define REASON_EVENT_SHIFT    (EVENT_REASON_ACCOUNT_CREDIT-3)
//+------------------------------------------------------------------+
//| Integer-Eigenschaften von Ereignissen                            |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_INTEGER
  {
   EVENT_PROP_TYPE_EVENT = 0,                               // Handelsereignistyp des Kontos (von der Enumeration ENUM_TRADE_EVENT)
   EVENT_PROP_TIME_EVENT,                                   // Zeitereignis in Millisekunden
   EVENT_PROP_STATUS_EVENT,                                 // Ereignisstatur (von der Enumeration ENUM_EVENT_STATUS)
   EVENT_PROP_REASON_EVENT,                                 // Ereignisgrund (von der Enumeration ENUM_EVENT_REASON)
   EVENT_PROP_TYPE_DEAL_EVENT,                              // Ereignis-Typ des Deals
   EVENT_PROP_TICKET_DEAL_EVENT,                            // Ereignis-Ticket des Deals
   EVENT_PROP_TYPE_ORDER_EVENT,                             // Auftragstyp auf Basis eines Auftrags des dadurch geöffneten Deal-Ereignisses (der letzte Auftrag zur Position)
   EVENT_PROP_TICKET_ORDER_EVENT,                           // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
   EVENT_PROP_TIME_ORDER_POSITION,                          // Zeit eines Auftrags auf Basis des dadurch eröffneten Positions-Deals (der erste Positionsauftrag)
   EVENT_PROP_TYPE_ORDER_POSITION,                          // Auftragstyp Type auf Basis der Eröffnung der Position (der erste Auftrag zur Position)
   EVENT_PROP_TICKET_ORDER_POSITION,                        // Ticket eines Auftrags auf Basis des dadurch eröffneten Positions-Deal (der erste Positionsauftrag)
   EVENT_PROP_POSITION_ID,                                  // Positions-ID
   EVENT_PROP_POSITION_BY_ID,                               // ID der entgegengesetzten Position
   EVENT_PROP_MAGIC_ORDER,                                  // Auftrag/Deal/Position Magicnummer
  }; 
#define EVENT_PROP_INTEGER_TOTAL (14)                       // Gesamtzahl der Integer-Eigenschaften der Ereignisse
//+------------------------------------------------------------------+
//| Double-Eigenschaften von Ereignissen                             |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_DOUBLE
  {
   EVENT_PROP_PRICE_EVENT = (EVENT_PROP_INTEGER_TOTAL),     // Preis, bei dem ein Ereignis auftritt
   EVENT_PROP_PRICE_OPEN,                                   // Order/Deal/Position Eröffnungspreis
   EVENT_PROP_PRICE_CLOSE,                                  // Order/Deal/Position Schließpreis
   EVENT_PROP_PRICE_SL,                                     // StopLoss Order/Deal/Position
   EVENT_PROP_PRICE_TP,                                     // TakeProfit Order/Deal/Position
   EVENT_PROP_VOLUME_INITIAL,                               // Verlangtes Volumen
   EVENT_PROP_VOLUME_EXECUTED,                              // Ausgeführtes Volumen
   EVENT_PROP_VOLUME_CURRENT,                               // Verbliebenes Volumen
   EVENT_PROP_PROFIT                                        // Gewinn
  };
#define EVENT_PROP_DOUBLE_TOTAL  (9)                        // Gesamtzahl der Double-Eigenschaften
//+------------------------------------------------------------------+
//| String-Eigenschaften von Ereignissen                             |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_STRING
  {
   EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // Auftragssymbol
  };
#define EVENT_PROP_STRING_TOTAL     (1)                     // Gesamtzahl der String-Eigenschaften
//+------------------------------------------------------------------+
//| Mögliche Sortierkriterien von Ereignissen                        |
//+------------------------------------------------------------------+
#define FIRST_EVN_DBL_PROP       (EVENT_PROP_INTEGER_TOTAL)
#define FIRST_EVN_STR_PROP       (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL)
enum ENUM_SORT_EVENTS_MODE
  {
   //--- Sortieren nach den Integer-Eigenschaften
   SORT_BY_EVENT_TYPE_EVENT            = 0,                    // Sortieren nach Ereignis-Typ
   SORT_BY_EVENT_TIME_EVENT            = 1,                    // Sortieren nach Ereignis-Zeit
   SORT_BY_EVENT_STATUS_EVENT          = 2,                    // Sortieren nach Ereignis-Status (aus der Enumeration ENUM_EVENT_STATUS)
   SORT_BY_EVENT_REASON_EVENT          = 3,                    // Sortieren nach Ereignis-Grund (aus der Enumeration ENUM_EVENT_REASON)
   SORT_BY_EVENT_TYPE_DEAL_EVENT       = 4,                    // Sortieren nach Ereignis-Typ des Deals
   SORT_BY_EVENT_TICKET_DEAL_EVENT     = 5,                    // Sortieren nach Ticket des Deal-Ereignisses
   SORT_BY_EVENT_TYPE_ORDER_EVENT      = 6,                    // Sortieren nach Auftragstyp auf Basis des Deal-Ereignisses (der letzte Auftrag zur Position)
   SORT_BY_EVENT_TYPE_ORDER_POSITION   = 7,                    // Sortieren nach Auftragstyp auf Basis der Eröffnung der Position (der erste Auftrag zur Position)
   SORT_BY_EVENT_TICKET_ORDER_EVENT    = 8,                    // Sortieren nach dem Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
   SORT_BY_EVENT_TICKET_ORDER_POSITION = 9,                    // Sortieren nach dem Ticket eines Auftrags auf Basis des dadurch eröffneten Positions-Deal (der erste Positionsauftrag)
   SORT_BY_EVENT_POSITION_ID           = 10,                   // Sortieren nach der Positions-ID
   SORT_BY_EVENT_POSITION_BY_ID        = 11,                   // Sortieren nach der entgegengesetzten Positions-ID
   SORT_BY_EVENT_MAGIC_ORDER           = 12,                   // Sortieren nach Auftrag/Deal/Position Magicnummer
   SORT_BY_EVENT_TIME_ORDER_POSITION   = 13,                   // Sortieren nach der Zeit des Auftrages auf Basis des dadurch eröffneten Positions-Deals (der erste Positionsauftrag)
   //--- Sortieren nach den Double-Eigenschaften
   SORT_BY_EVENT_PRICE_EVENT        =  FIRST_EVN_DBL_PROP,     // Sortieren nach einem Preis, bei dem ein Ereignis auftrat
   SORT_BY_EVENT_PRICE_OPEN         =  FIRST_EVN_DBL_PROP+1,   // Sortieren nach Eröffnungspreis der Position
   SORT_BY_EVENT_PRICE_CLOSE        =  FIRST_EVN_DBL_PROP+2,   // Sortieren nach Schließpreis der Position
   SORT_BY_EVENT_PRICE_SL           =  FIRST_EVN_DBL_PROP+3,   // Sortieren nach dem StopLoss der Position
   SORT_BY_EVENT_PRICE_TP           =  FIRST_EVN_DBL_PROP+4,   // Sortieren nach dem TakeProfit der Position
   SORT_BY_EVENT_VOLUME_INITIAL     =  FIRST_EVN_DBL_PROP+5,   // Sortieren nach Anfangsvolumen
   SORT_BY_EVENT_VOLUME             =  FIRST_EVN_DBL_PROP+6,   // Sortieren nach aktuellen Volumen
   SORT_BY_EVENT_VOLUME_CURRENT     =  FIRST_EVN_DBL_PROP+7,   // Sortieren nach verbliebenen Volumen
   SORT_BY_EVENT_PROFIT             =  FIRST_EVN_DBL_PROP+8,   // Sortieren nach Gewinn
   //--- Sortieren nach den String-Eigenschaften
   SORT_BY_EVENT_SYMBOL             =  FIRST_EVN_STR_PROP      // Sortieren nach Auftrag/Deal/Position/Quelle Symbol
  };
//+------------------------------------------------------------------+

Hier haben wir alle möglichen Ereignisobjektzustände (ähnlich wie im ersten Artikel beschrieben), Ereignisauftrittsgründe, alle Ereigniseigenschaften und Kriterien für die Sortierung von Ereignissen zur Suche nach Eigenschaften. All dies ist bereits aus den vorangegangenen Artikeln bekannt. Wir können jederzeit an den Anfang zurückkehren und die Daten zur Klärung aktualisieren.

Neben dem Ereignisstatus, der allgemeine Ereignisdaten liefert, enthält der Ereignisgrund (ENUM_EVENT_REASON) bereits alle Details zu einer bestimmten Ereignisherkunft.
Wenn ein Ereignis beispielsweise den Status einer Marktposition (EVENT_STATUS_MARKET_POSITION) hat, wird der Grund für das Auftreten des Ereignisses im Objektfeld EVENT_PROP_REASON_EVENT angegeben. Es kann entweder eine Pending Order Aktivierung (EVENT_REASON_ACTIVATED_PENDING) oder das Eröffnen einer Position durch eine Market Order (EVENT_REASON_DONE) sein. Dabei werden auch folgende Nuancen berücksichtigt: Wird eine Position teilweise eröffnet (nicht das gesamte Pending oder Marktordervolumen wurde ausgeführt), ist der Ereignisgrund EVENT_REASON_ACTIVATED_PENDING_PARTIALLY oder EVENT_REASON_DONE_PARTIALLY etc.
Ein Ereignisobjekt enthält somit die gesamten Daten über das Ereignis und einen Auftrag, der es ausgelöst hat. Darüber hinaus liefern historische Ereignisse Daten über zwei Aufträge — den ersten Positionsauftrag und den Auftrag die Position zu schließen.
So ermöglichen uns die Daten über Aufträge, Deals und die Positionen selbst im Ereignisobjekt die Verfolgung der gesamten Kette der Positionsereignisse in der gesamten Geschichte ihrer Existenz — vom Öffnen bis zum Schließen.

Die Enumerationskonstanten ENUM_EVENT_REASON werden so angeordnet und nummeriert, dass der Ereignisstatus "Deal" der Deal-Typ auf die Enumeration ENUM_DEAL_TYPE fällt, falls der Dealtyp DEAL_TYPE_SELL überschreitet. So kommen wir schlussendlich zu den Typen der Saldenoperationen. Die Beschreibung der Saldenoperation wird bei der Definition der Dealtyp in der zur Anlage vorbereiteten Klasse an den Ereignisgrund gesendet.
Die Verschiebung, die dem Dealtyp hinzugefügt werden soll, wird in der Makroersetzung #define REASON_EVENT_SHIFT gesetzt. Es ist notwendig, die Saldooperation in der Aufzählung ENUM_EVENT_REASON einzugliedern.

Fügen wir die Funktionen hinzu, die Beschreibungen von order, position und deal types zurückgeben, sowie die Funktion, die den Positionsnamen abhängig von der Art der Öffnung eines Auftrags zurückgibt. Alle Funktionen werden der Datei DELib.mqh in der Bibliothek Services hinzugefügt. Dies ermöglicht eine komfortable Ausgabe von Aufträgen, Positionen und Deals.

//+------------------------------------------------------------------+
//| Rückgabe des Auftragsnamen                                       |
//+------------------------------------------------------------------+
string OrderTypeDescription(const ENUM_ORDER_TYPE type)
  {
   string pref=(#ifdef __MQL5__ "Market order" #else "Position" #endif );
   return
     (
      type==ORDER_TYPE_BUY_LIMIT       ?  "Buy Limit"                                                 :
      type==ORDER_TYPE_BUY_STOP        ?  "Buy Stop"                                                  :
      type==ORDER_TYPE_SELL_LIMIT      ?  "Sell Limit"                                                :
      type==ORDER_TYPE_SELL_STOP       ?  "Sell Stop"                                                 :
   #ifdef __MQL5__
      type==ORDER_TYPE_BUY_STOP_LIMIT  ?  "Buy Stop Limit"                                            :
      type==ORDER_TYPE_SELL_STOP_LIMIT ?  "Sell Stop Limit"                                           :
      type==ORDER_TYPE_CLOSE_BY        ?  TextByLanguage("Закрывающий ордер","Order for closing by")  :  
   #else 
      type==ORDER_TYPE_BALANCE         ?  TextByLanguage("Балансовая операция","Balance operation")   :
      type==ORDER_TYPE_CREDIT          ?  TextByLanguage("Кредитная операция","Credit operation")     :
   #endif 
      type==ORDER_TYPE_BUY             ?  pref+" Buy"                                                 :
      type==ORDER_TYPE_SELL            ?  pref+" Sell"                                                :  
      TextByLanguage("Неизвестный тип ордера","Unknown order type")
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Positionsnamen                                      |
//+------------------------------------------------------------------+
string PositionTypeDescription(const ENUM_POSITION_TYPE type)
  {
   return
     (
      type==POSITION_TYPE_BUY    ? "Buy"  :
      type==POSITION_TYPE_SELL   ? "Sell" :  
      TextByLanguage("Неизвестный тип позиции","Unknown position type")
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Dealnamens                                          |
//+------------------------------------------------------------------+
string DealTypeDescription(const ENUM_DEAL_TYPE type)
  {
   return
     (
      type==DEAL_TYPE_BUY                       ?  TextByLanguage("Сделка на покупку","Buy deal") :
      type==DEAL_TYPE_SELL                      ?  TextByLanguage("Сделка на продажу","Sell deal") :
      type==DEAL_TYPE_BALANCE                   ?  TextByLanguage("Балансовая операция","Balance operation") :
      type==DEAL_TYPE_CREDIT                    ?  TextByLanguage("Начисление кредита","Credit") :
      type==DEAL_TYPE_CHARGE                    ?  TextByLanguage("Дополнительные сборы","Additional charge") :
      type==DEAL_TYPE_CORRECTION                ?  TextByLanguage("Корректирующая запись","Correction") :
      type==DEAL_TYPE_BONUS                     ?  TextByLanguage("Перечисление бонусов","Bonus") :
      type==DEAL_TYPE_COMMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
      type==DEAL_TYPE_COMMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") :
      type==DEAL_TYPE_COMMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") :
      type==DEAL_TYPE_COMMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") :
      type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") :
      type==DEAL_TYPE_INTEREST                  ?  TextByLanguage("Начисления процентов на свободные средства","Agency commission charged at the end of month") :
      type==DEAL_TYPE_BUY_CANCELED              ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
      type==DEAL_TYPE_SELL_CANCELED             ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
      type==DEAL_DIVIDEND                       ?  TextByLanguage("Начисление дивиденда","Dividend operations") :
      type==DEAL_DIVIDEND_FRANKED               ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      type==DEAL_TAX                            ?  TextByLanguage("Начисление налога","Tax charges") : 
      TextByLanguage("Неизвестный тип сделки","Unknown deal type")
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Positionstyps nach dem Auftragstyp                  |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE PositionTypeByOrderType(ENUM_ORDER_TYPE type_order)
  {
   if(
      type_order==ORDER_TYPE_BUY             ||
      type_order==ORDER_TYPE_BUY_LIMIT       ||
      type_order==ORDER_TYPE_BUY_STOP
   #ifdef __MQL5__                           ||
      type_order==ORDER_TYPE_BUY_STOP_LIMIT
   #endif 
     ) return POSITION_TYPE_BUY;
   else if(
      type_order==ORDER_TYPE_SELL            ||
      type_order==ORDER_TYPE_SELL_LIMIT      ||
      type_order==ORDER_TYPE_SELL_STOP
   #ifdef __MQL5__                           ||
      type_order==ORDER_TYPE_SELL_STOP_LIMIT
   #endif 
     ) return POSITION_TYPE_SELL;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Beim Testen der Klasse der Ereigniskollektion wurde ein sehr unangenehmes Problem entdeckt: Beim Erstellen der Listen der Aufträge und Deals im Terminal mit HistorySelect() und dem anschließenden Zugriff auf die neuen Elemente der Listen stellte ich fest, dass die Aufträge nicht in der Reihenfolge ihres Auftretens, sondern nach ihrer Platzierungszeit gereiht werden. Lassen Sie es mich erklären:

  1. Eine Position eröffnen,
  2. Eine Pending-Order sofort platzieren
  3. Schließen eines Teils einer Position
  4. Warten, bis eine Pending-Order aktiviert wird

Es wird erwartet, dass die Reihenfolge der Ereignisse in der Historie wie folgt ist:
Öffnen einer Position, Platzieren einer Pending-Order, Teilschließung, Aktivierung der Pending-Order — in der Reihenfolge der Durchführung von Operationen im Ablauf der Zeit. Aber es stellte sich heraus, dass die Reihenfolge der Ereignisse in der gemeinsamen Reihenfolge und Dealhistorie wie folgt ist:

  1. Öffnen einer Position
  2. Platzieren der Pending-Order
  3. Aktivieren der Pending-Order
  4. Teilschließung

Mit anderen Worten, die Geschichten der Aufträge und Deals leben ihr eigenes Leben innerhalb des Terminals und korrelieren nicht miteinander, was auch sinnvoll ist, da es sich um zwei Listen mit jeweils eigener Historie handelt.

Die Klasse der Kollektionen der Aufträge und Deals wird so gebildet, dass beim Ändern einer der Listen (Aufträge oder Deals) das letzte Ereignis auf dem Konto gelesen wird, um die Historie nicht ständig zu scannen, was sehr teuer wäre. Aber in Anbetracht des oben Gesagten und bei der Durchführung von Handelsoperationen verfolgen wir nicht die Reihenfolge der Aktionen. Wir geben einfach eine Bestellung auf und warten auf ihre Aktivierung. Nach der Eröffnung einer Position arbeiten wir ausschließlich mit ihr. In diesem Fall sind alle Ereignisse in der erforderlichen Reihenfolge zu sortieren, die eine Verfolgung ermöglicht. Dies ist jedoch nicht ausreichend. Wir müssen in jeder Reihenfolge arbeiten, und das Programm sollte in der Lage sein, das richtige Ereignis zu finden und genau darauf zu zeigen.

Basierend auf dem oben Gesagten habe ich die Klasse der Kollektionen der historischen Aufträge und Ereignisse verbessert. Wenn nun ein Ereignis außer der Reihe auftritt, findet die Klasse den notwendigen Auftrag, erzeugt ihr Objekt und stellt es in die Liste als letztes, so dass die Klasse der Ereigniskollektion immer in der Lage ist, das zuletzt aufgetretene Ereignis genau zu definieren.

Um die Funktion zu implementieren, fügen wir drei neue Methoden zum 'private' Bereich der Klasse für historische Aufträge und Deals hinzu:

//--- Liefert das Flag des Auftragsobjekts nach Typ und Ticket in der Liste der historischen Aufträge und Deals.
   bool              IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type);
//--- Rückgabe des "verlorenen" Auftragstyps und des Tickets
   ulong             OrderSearch(const int start,ENUM_ORDER_TYPE &order_type);
//--- Erstellen der Auftragsobjektes und Eintragen in der Liste
   bool              CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type);

und ihre Umsetzung auch außerhalb des Klassenkörpers.

Die Methode gibt das Flag des in der Liste vorhandenen Auftragsobjekts durch sein Ticket und seinen Typ zurück:

//+-----------------------------------------------------------------------------+
//| Liefert das Flag des aktuellen Auftragsobjektes der Liste nach Typ & Ticket |
//+-----------------------------------------------------------------------------+
bool CHistoryCollection::IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type)
  {
   CArrayObj* list=dynamic_cast<CListObj*>(&this.m_list_all_orders);
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,type,EQUAL);
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,order_ticket,EQUAL);
   return(list.Total()>0);
  }
//+------------------------------------------------------------------+

Den Zeiger auf die Liste mit dem dynamischen Typecasting erstellen (senden Sie die CArrayObj-Liste an die Klasse CSelect, während Kollektionslisten vom Typ CListObj sind und von CArrayObj abgeleitet werden)
Nur die Aufträge behalten , die den Typ haben, der der Methode durch die Eingabe übergeben wurde.
Nur den Auftrag behalten , der das Ticket hat, das der Methode übergeben wurde.
Wenn ein solcher Auftrag existiert (die Liste ist größer als Null), geben wir true zurück.

Die Methode, die einen Typ und eine Methode eines Auftrags zurückgibt, der nicht der letzte in der Liste des Terminals, aber nicht in der Kollektionsliste ist:

//+------------------------------------------------------------------+
//| Liefert den Typ und das Ticket der "verlorenen" Auftrags zurück  |
//+------------------------------------------------------------------+
ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type)
  {
   ulong order_ticket=0;
   for(int i=start-1;i>=0;i--)
     {
      ulong ticket=::HistoryOrderGetTicket(i);
      if(ticket==0)
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE);
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
   return order_ticket;
  }
//+------------------------------------------------------------------+

Der Index des letzten Auftrags wird an die Terminal-Auftragsliste übergeben. Da der Index die bereits in der Kollektion vorhandene Aufträge angibt, sollte die Suchschleife aus der vorherigen Reihenfolge in der Liste (start-1) gestartet werden.
Da sich der benötigte Auftrag in der Regel am Ende der Liste befindet, suchen Sie mit der Methode IsPresentOrderInList() nach einer Reihenfolge mit einem Ticket und einem in der Kollektion fehlenden Typ in der Schleife vom Listenende aus. Wenn der Auftrag in der Kollektion vorhanden ist, überprüfen Sie den nächsten. Sobald ein Auftrag in der Kollektion fehlt, wird dessen Ticket und Typ geschrieben und an das aufrufende Programm zurückgesendet. Das Ticket wird durch die Methode result zurückgegeben, während der Typ in der Variable über den Link zurückgegeben wird.

Da wir nun an mehreren Stellen innerhalb der Klasse Auftragsobjekte erstellen müssen (bei der Definition einer neuen Bestellung und bei der Suche nach einer "verlorenen"), machen wir eine separate Methode, um ein Auftragsobjekt zu erstellen und es in die Kollektionsliste aufzunehmen:

//+------------------------------------------------------------------+
//| Erstellen eines Auftragsobjekts und Platzieren in der Liste      |
//+------------------------------------------------------------------+
bool CHistoryCollection::CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type)
  {
   COrder* order=NULL;
   if(order_type==ORDER_TYPE_BUY)
     {
      order=new CHistoryOrder(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_BUY_LIMIT)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_BUY_STOP)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_SELL)
     {
      order=new CHistoryOrder(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_SELL_LIMIT)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_SELL_STOP)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
#ifdef __MQL5__
   else if(order_type==ORDER_TYPE_BUY_STOP_LIMIT)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_SELL_STOP_LIMIT)
     {
      order=new CHistoryPending(order_ticket);
      if(order==NULL)
         return false;
     }
   else if(order_type==ORDER_TYPE_CLOSE_BY)
     {
      order=new CHistoryOrder(order_ticket);
      if(order==NULL)
         return false;
     }
#endif 
   if(this.m_list_all_orders.InsertSort(order))
      return true;
   else
     {
      delete order;
      return false;
     }
   return false;
  }
//+------------------------------------------------------------------+

Hier ist alles einfach und klar: Die Methode erhält Auftragsticket und -typ, und je nach Auftragsart wird ein neues Auftragsobjekt angelegt. Wenn das Objekt nicht erstellt werden konnte, wird sofort false zurückgegeben. Wenn das Objekt erfolgreich erstellt wurde, wird es in die Kollektion gestellt und true wird zurückgegeben. Wenn es nicht in die Kollektion platziert werden konnte, wird ein neu erstelltes Objekt entfernt und false zurückgegeben.

Ändern wir die Methode Refresh() der Klasse der Kollektion, da der "Verlust" eines benötigen Auftrags verarbeitet werden soll:

//+------------------------------------------------------------------+
//| Aktualisieren der Liste der Aufträge und Deals                   |
//+------------------------------------------------------------------+
void CHistoryCollection::Refresh(void)
  {
#ifdef __MQL4__
   int total=::OrdersHistoryTotal(),i=m_index_order;
   for(; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType();
      //--- Geschlossenen Positionen und Salden-/Korrekturbuchungen
      if(order_type<ORDER_TYPE_BUY_LIMIT || order_type>ORDER_TYPE_SELL_STOP)
        {
         CHistoryOrder *order=new CHistoryOrder(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list"));
            delete order;
           }
        }
      else
        {
         //--- Entfernte Pending-Order
         CHistoryPending *order=new CHistoryPending(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))this.m_list_all_orders.Type()
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list"));
            delete order;
           }
        }
     }
//---
   int delta_order=i-m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   this.m_is_trade_event=(this.m_delta_order!=0 ? true : false);
//--- __MQL5__
#else 
   if(!::HistorySelect(0,END_TIME)) return;
//--- Orders
   int total_orders=::HistoryOrdersTotal(),i=m_index_order;
   for(; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY)
        {
         //--- Wenn es keinen Auftrag mit diesem Typ und diesem Ticket in der Liste gibt, wird ein Auftragsobjekt erstellt und in die Liste eingetragen
         if(!this.IsPresentOrderInList(order_ticket,type))
           {
            if(!this.CreateNewOrder(order_ticket,type))
               ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list"));
           }
         //--- Gibt es einen solchen Auftrag in der Liste, heißt dass, der benötigte Auftrag ist nicht der letzte in der historischen Liste Let's find it
         else
           {
            ENUM_ORDER_TYPE type_lost=WRONG_VALUE;
            ulong ticket_lost=this.OrderSearch(i,type_lost);
            if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost))
               ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list"));
           }
        }
      else
        {
         //--- Wenn es keine Pending-Order mit diesem Typ und diesem Ticket in der Liste gibt, wird ein Auftragsobjekt erstellt und in die Liste eingetragen
         if(!this.IsPresentOrderInList(order_ticket,type))
           {
            if(!this.CreateNewOrder(order_ticket,type))
               ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list"));
           }
         //--- Gibt es einen solchen Auftrag in der Liste, heißt dass, der benötigte Auftrag ist nicht der letzte in der historischen Liste Let's find it
         else
           {
            ENUM_ORDER_TYPE type_lost=WRONG_VALUE;
            ulong ticket_lost=this.OrderSearch(i,type_lost);
            if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost))
               ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list"));
           }
        }
     }
//--- Sichern des Index des zuletzt ergänzten Auftrags und der Änderung im Vergleich zur vorherigen Prüfung
   int delta_order=i-this.m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;

//--- Deals
   int total_deals=::HistoryDealsTotal(),j=m_index_deal;
   for(; j<total_deals; j++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(j);
      if(deal_ticket==0) continue;
      CHistoryDeal *deal=new CHistoryDeal(deal_ticket);
      if(deal==NULL) continue;
      if(!this.m_list_all_orders.InsertSort(deal))
        {
         ::Print(DFUN,TextByLanguage("Не удалось добавить сделку в список","Could not add deal to list"));
         delete deal;
        }
     }
//--- Sichern des Index des zuletzt ergänzten Deals und der Änderung im Vergleich zur vorherigen Prüfung
   int delta_deal=j-this.m_index_deal;
   this.m_index_deal=j;
   this.m_delta_deal=delta_deal;
//--- Setzen des Flags für neue Ereignisse in der Historie
   this.m_is_trade_event=(this.m_delta_order+this.m_delta_deal);
#endif 
  }
//+------------------------------------------------------------------+

Der Block zur Bearbeitung neuer Aufträge für MQL5 wurde in der Methode geändert. Alle vorgenommenen Änderungen werden durch Kommentare beschrieben und im aufgelisteten Text hervorgehoben.

Fügen wir die Methodendefinition hinzu, um nach ähnlichen Aufträgen im 'public' Bereich der Klasse COrder zu suchen:

//---- Vergleich von COrder nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen)
   bool              IsEqual(COrder* compared_order) const;

und seine Implementierung auch außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Vergleichen von COrder-Objekte nach allen Eigenschaften          |
//+------------------------------------------------------------------+
bool COrder::IsEqual(COrder *compared_order) const
  {
   int beg=0, end=ORDER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; 
     }
   beg=end; end+=ORDER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; 
     }
   beg=end; end+=ORDER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode prüft alle Eigenschaften des aktuellen Auftragsobjekts und die vom Zeiger in einer Schleife der an die Methode übergebene verglichene Auftrag.
Sobald eine der Eigenschaften der aktuellen Bestellung, die nicht gleich der gleichen Eigenschaft der verglichenen Bestellung ist, erkannt wird, wird false zurückgegeben, was bedeutet, dass die Aufträge nicht gleich sind.


Ereignisklassen

Die Vorbereitungsphase ist abgeschlossen. Beginnen wir mit dem Erstellen von Klassen von Ereignisobjekten.

Wir werden genau das gleiche tun wie beim Anlegen von Auftragsklassen. Wir werden eine grundlegende Ereignisklasse und fünf Nachkommenklassen entwickeln, die durch ihre Zustände beschrieben werden:

Wir erstellen im zuvor erstellten Ordner Events des Bibliotheksverzeichnisses Objects eine neue Klasse CEvent, die von der Basisklasse CObject abgeleitet wird. In der neu erstellten Klassenvorlage erstellen wir die notwendigen Einbindungen der Servicefunktionendatei, der Klassen der Auftragskollektionen sowie der 'private' und 'protected' Klassenmitglieder und Methoden:

//+------------------------------------------------------------------+
//|                                                        Event.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Notwendig für mql4
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "\..\..\Services\DELib.mqh"
#include "..\..\Collections\HistoryCollection.mqh"
#include "..\..\Collections\MarketCollection.mqh"
//+------------------------------------------------------------------+
//| Abstrakte Ereignisklasse                                         |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // Ereigniscode
//--- Rückgabe des Arrayindex des Ereignisses mit den (1) Double- und (2) String-Eigenschaften
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // Handelsereignis
   long              m_chart_id;                                     // Chart-ID des Steuerprogramms
   int               m_digits_acc;                                   // Dezimalstellen der Kontowährung
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // Integer-Eigenschaften des Ereignisses
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // Double-Eigenschaften des Ereignisses
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // String-Eigenschaften des Ereignisses
//--- Rückgabe des Vorhandenseins des Flags des Handelsereignisses
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;            }

   //--- 'Protected' Konstruktor
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- Standardmäßiger Konstruktor
                     CEvent(void){;}
 
//--- Setzen der (1) Integer-, (2) Double- und (3) String-Eigenschaften des Ereignisses
   void              SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;    }
//--- Rückgabe von (1) Integer-, (2) Double- und (3) String-Eigenschaften aus dem Array der Eigenschaften
   long              GetProperty(ENUM_EVENT_PROP_INTEGER property)      const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_EVENT_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_EVENT_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];   }

//--- Rückgabe des Flags des Ereignisses, das die Eigenschaften unterstützt
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property)         { return true; }

//--- Setzen der Chart-ID des Steuerprogramms
   void              SetChartID(const long id)                                { this.m_chart_id=id;                                    }
//--- Dekodieren des Ereigniscodes und setzen des Handelsereignisses, (2) Rückgabe des Handelsereignisses
   void              SetTypeEvent(void);
   ENUM_TRADE_EVENT  TradeEvent(void)                                   const { return this.m_trade_event;                             }
//--- Senden des Ereignisses an das Chart (Implementation in der abgeleiteten Klasse)
   virtual void      SendEvent(void) {;}

//--- Vergleichen der CEvent-Objekte nach der angegebenen Eigenschaft (für das Sorteiren der Liste nach der angegebenen Eigenschaft des Ereignisobjektes)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//---- Vergleich von CEvent-Objekten nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen)
   bool              IsEqual(CEvent* compared_event) const;
//+------------------------------------------------------------------+
//| Methode für einen vereinfachten Zugriff auf die Eigenschaften    |
//+------------------------------------------------------------------+
//--- Rückgabe von (1) Ereignis-Typ, (2) Ereignis-Zeit in Millisekunden, (3) Ereignis-Status, (4) Ereignis-Grund, (5) Dealtyp, (6) Deal-Ticket, 
//--- (7) Auftragstyp des dadurch geöffneten Deal-Ereignisses, (8) Auftragstyp der Positionseröffnung, (9) letztes Auftragsticket der Position, 
//--- (10) erstes Auftragsticket der Position, (11) Positions-ID, (12) ID der entgegengesetzten Position, (13) Magicnummer, (14) Eröffnungszeit der Position

   ENUM_TRADE_EVENT  TypeEvent(void)                                    const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT);     }
   long              TimeEvent(void)                                    const { return this.GetProperty(EVENT_PROP_TIME_EVENT);                       }
   ENUM_EVENT_STATUS Status(void)                                       const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);  }
   ENUM_EVENT_REASON Reason(void)                                       const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT);  }
   long              TypeDeal(void)                                     const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);                  }
   long              TicketDeal(void)                                   const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT);                }
   long              TypeOrderEvent(void)                               const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT);                 }
   long              TypeOrderPosition(void)                            const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION);              }
   long              TicketOrderEvent(void)                             const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT);               }
   long              TicketOrderPosition(void)                          const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION);            }
   long              PositionID(void)                                   const { return this.GetProperty(EVENT_PROP_POSITION_ID);                      }
   long              PositionByID(void)                                 const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID);                   }
   long              Magic(void)                                        const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER);                      }
   long              TimePosition(void)                                 const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION);              }
   
//--- Rückgabe von (1) Preis bei den ein Ereignis auftritt, (2) Eröffnungspreis, (3) Schließpreis,
//--- (4) StopLoss, (5) TakeProfit, (6) Gewinn, (7) verlangtes Volumen, (8), ausgeführtes Volumen, (9) verbliebenes Volumen
   double            PriceEvent(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_EVENT);                      }
   double            PriceOpen(void)                                    const { return this.GetProperty(EVENT_PROP_PRICE_OPEN);                       }
   double            PriceClose(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE);                      }
   double            PriceStopLoss(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_SL);                         }
   double            PriceTakeProfit(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_TP);                         }
   double            Profit(void)                                       const { return this.GetProperty(EVENT_PROP_PROFIT);                           }
   double            VolumeInitial(void)                                const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL);                   }
   double            VolumeExecuted(void)                               const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED);                  }
   double            VolumeCurrent(void)                                const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT);                   }
   
//--- Rückgabe eines Symbols
   string            Symbol(void)                                       const { return this.GetProperty(EVENT_PROP_SYMBOL);                           }
   
//+------------------------------------------------------------------+
//| Beschreibung der Objekteigenschaften des Auftrags                |
//+------------------------------------------------------------------+
//--- Rückgabe der Beschreibung der (1) Integer-, (2) Double- und (3) String-Eigenschaft des Auftrages
   string            GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_STRING property);
//--- Rückgabe von (1) Status und (2) Typ des Ereignisses
   string            StatusDescription(void)          const;
   string            TypeEventDescription(void)       const;
//--- Rückgabe von (1) Order/Deal/Position, (2) übergeordnete Order, (3) Position
   string            TypeOrderDescription(void)       const;
   string            TypeOrderBasedDescription(void)  const;
   string            TypePositionDescription(void)    const;
//--- Rückgabe des Namens des Grundes von Order/Deal/Position
   string            ReasonDescription(void)          const;

//--- Anzeigen (1) Beschreibung der Auftragseigenschaften (full_prop=true - alle Eigenschaften, false - nur die unterstützten),
//--- (2) kurze Ereignisnachrichten (implementiert in abgeleiteten Klassen) im Journal
   void              Print(const bool full_prop=false);
   virtual void      PrintShort(void) {;}
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

Der Konstruktor empfängt den Ereignisstatus, Handelsereigniscode und Auftrags- oder Dealticket, das das Ereignis ausgelöst hat.

Hier ist fast alles einem geschützten Konstruktor der zuvor betrachteten Klasse COrder im ersten Teil der Bibliotheksbeschreibung ähnlich.

Der Unterschied besteht darin, dass im geschützten Klassenkonstruktor nur zwei Ereignis-Eigenschaften gefüllt werden. Dies sind der Ereignisstatus und das Ticket eines Auftrags/Deal, der das Ereignis ausgelöst hat. Der Ereignistyp wird erkannt und in der Klassenmethode SetTypeEvent() gespeichert, basierend auf dem an den Konstruktor übergebenen Ereigniscode. Alle anderen Ereigniseigenschaften werden aus dem Status von Aufträgen und Deals, die am Ereignis beteiligt sind, erkannt und durch die entsprechenden Klassenmethoden separat gesetzt. Dies geschieht, weil Ereignisse in der Klasse der Ereignis-Kollektion mit der Methode, die alle Eigenschaften für ein neu erzeugtes Ereignis festlegt, erkannt werden sollen.

Wir haben den Ereigniscode (m_event_code), sowie seine Füllung und Interpretation im vierten Teil der Bibliotheksbeschreibung berücksichtigt. Wir haben es hier aus der Klasse CEngine verschoben, da es temporär in die Bibliotheksbasisklasse platziert wurde, um die Arbeit mit Ereignissen zu überprüfen. Jetzt wird es in der Klasse der Ereigniskollektion berechnet und beim Anlegen eines Ereignisobjekts an den Klassenkonstrukteur übergeben.
Das Handelsereignis (m_trade_event) selbst wird durch Dekodierung des Ereigniscodes in der Methode SetTypeEvent() verarbeitet. Wir haben die Dekodierungsmethode des Ereigniscodes bereits im vierten Artikel beschrieben.
Wir benötigen die Chart ID des Steuerprogramms ( m_chart_id), um nutzerdefinierte Nachrichten über Ereignisse an sie zu senden.
Die Anzahl der Nachkommastellen für die Kontowährung ( m_digit_acc) ist notwendig für die korrekte Anzeige von Meldungen über Ereignisse im Journal.

Die Methoden zum Vergleichen der Compare() und IsEqual() Objektereignisseigenschaften sind recht einfach und klar. Wir haben die Methode Compare() im ersten Teil der Bibliotheksbeschreibung berücksichtigt. Sie war ähnlich wie der des Objekts COrder. Im Vergleich zu der ersten Methode, die zwei Objekte nur durch eine der Eigenschaften vergleicht, vergleicht IsEqual() alle Felder von beiden Objekten. Wenn alle Felder der beiden Objekte gleich sind (jede Eigenschaft des aktuellen Objekts ist gleich der entsprechenden Eigenschaft des verglichenen Objekts), dann sind beide Objekte identisch. Die Methode überprüft alle Eigenschaften der beiden Objekte in einer Schleife und gibt false zurück, sobald ein Unterschied erkannt wird. Weitere Prüfungen machen keinen Sinn, da eine der Objekteigenschaften nicht länger gleich der entsprechenden Eigenschaft des verglichenen Objekts ist.

//+------------------------------------------------------------------+
//| Vergleich einer bestimmten Eigenschaft von CEvent-Objekten       |
//+------------------------------------------------------------------+
int CEvent::Compare(const CObject *node,const int mode=0) const
  {
   const CEvent *event_compared=node;
//--- Vergleich der Integer-Eigenschaften von zwei Ereignissen
   if(mode<EVENT_PROP_INTEGER_TOTAL)
     {
      long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- Vergleich der Integer-Eigenschaften von zwei Objekten
   if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL)
     {
      double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- Vergleich der String-Eigenschaften von zwei Objekten
   else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL)
     {
      string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+
//| Vergleichen der Ereignisse CEvent nach allen Eigenschaften       |
//+------------------------------------------------------------------+
bool CEvent::IsEqual(CEvent *compared_event) const
  {
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Lassen Sie uns einen genaueren Blick auf die Methode SetTypeEvent() werfen.

Alle notwendigen Prüfungen und Aktionen werden direkt in den Codekommentaren festgelegt:

//+------------------------------------------------------------------+
//| Dekodieren des Ereigniscodes und setzen des Handelsereignisses   |
//+------------------------------------------------------------------+
void CEvent::SetTypeEvent(void)
  {
//--- Pending-Order platziert (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Pending-Order entfernt (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Positionseröffnung (Prüfen mehrerer Flags im Ereigniscode)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED))
     {
      //--- Wenn die Pending-Order durch den Preis aktiviert wurde
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Pending-Order aktiviert" oder "Pending-Order teilweise aktiviert""
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Prüfen des Flags für ein teilweises Eröffnen und setzen des Handelsereignisses von "Position eröffnet" oder "Position teilweise eröffnet"
      this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL);
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Position geschlossen (Prüfen mehrerer Flags im Ereigniscode)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED))
     {
      //--- wenn eine Position durch StopLoss geschlossen wurde
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch StopLoss" oder "Position teilweise geschlossen durch StopLoss"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn die Position durch TakeProfit geschlossen wurde
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch TakeProfit" oder "Position teilweise geschlossen durch TakeProfit"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn eine Position durch eine Gegenposition geschlossen wurde
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch eine Gegenposition" oder "Position teilweise geschlossen durch eine Gegenposition"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn eine Position geschlossen wurde
      else
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen" oder "Position teilweise geschlossen"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
//--- Saldooperation auf dem Konto (Klärung des Ereignisses durch den Dealtyp)
   if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE)
     {
      //--- Initialisierung des Handelsereignisses
      this.m_trade_event=TRADE_EVENT_NO_EVENT;
      //--- Nehmen des Dealtyps
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);
      //--- Wenn der Deal eine Saldenoperationen ist
      if(deal_type==DEAL_TYPE_BALANCE)
        {
        //--- Prüfen des Deals-Gewinns und setzen des Ereignisses (Gelder zu- oder abbuchen)
         this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL);
        }
      //--- Buchungstyp des verbliebenen Saldos passt zur Enumeration ENUM_DEAL_TYPE beginnend mit DEAL_TYPE_CREDIT
      else if(deal_type>DEAL_TYPE_BALANCE)
        {
        //--- Setzen des Ereignisses
         this.m_trade_event=(ENUM_TRADE_EVENT)deal_type;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
  }
//+------------------------------------------------------------------+

Hier ist alles einfach: Ein Ereigniscode wird an die Methode übergeben und die Ereigniscodeflags werden dann überprüft. Wenn der Code das Häkchen hat, wird das entsprechende Handelsereignis eingetragen. Da der Ereigniscode mehrere Flags haben kann, werden alle möglichen Flags für das Ereignis geprüft und der Ereignistyp aus seiner Kombination definiert. Anschließend wird der Ereignistyp in die entsprechende Klassenvariable aufgenommen und in die Eigenschaft des Ereignisobjekts (EVENT_PROP_TYPE_EVENT) eingetragen.

Lassen Sie uns einen Blick auf die Auflistung der übrigen Klassenmethoden werfen:

//+------------------------------------------------------------------+
//| Liefert die Beschreibung der Integer-Eigenschaft des Ereignisses |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property)
  {
   return
     (
      property==EVENT_PROP_TYPE_EVENT              ?  TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription()                                                       :
      property==EVENT_PROP_TIME_EVENT              ?  TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property))                                    :
      property==EVENT_PROP_STATUS_EVENT            ?  TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\""                                             :
      property==EVENT_PROP_REASON_EVENT            ?  TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription()                                                   :
      property==EVENT_PROP_TYPE_DEAL_EVENT         ?  TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property))                     :
      property==EVENT_PROP_TICKET_DEAL_EVENT       ?  TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property)                                              :
      property==EVENT_PROP_TYPE_ORDER_EVENT        ?  TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property))    :
      property==EVENT_PROP_TYPE_ORDER_POSITION     ?  TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) :
      property==EVENT_PROP_TICKET_ORDER_POSITION   ?  TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property)              :
      property==EVENT_PROP_TICKET_ORDER_EVENT      ?  TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property)                               :
      property==EVENT_PROP_POSITION_ID             ?  TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property)                                       :
      property==EVENT_PROP_POSITION_BY_ID          ?  TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+" #"+(string)this.GetProperty(property)                  :
      property==EVENT_PROP_MAGIC_ORDER             ?  TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property)                                           :
      property==EVENT_PROP_TIME_ORDER_POSITION     ?  TextByLanguage("Время открытия позиции","Position open time")+": "+TimeMSCtoString(this.GetProperty(property))                  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe der Beschreibung der Double-Eigenschaft des Ereignisses |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL));
   return
     (
      property==EVENT_PROP_PRICE_EVENT       ?  TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) :
      property==EVENT_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg)                    :
      property==EVENT_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg)                   :
      property==EVENT_PROP_PRICE_SL          ?  TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg)                :
      property==EVENT_PROP_PRICE_TP          ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg)            :
      property==EVENT_PROP_VOLUME_INITIAL    ?  TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl)             :
      property==EVENT_PROP_VOLUME_EXECUTED   ?  TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl)          :
      property==EVENT_PROP_VOLUME_CURRENT    ?  TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl)          :
      property==EVENT_PROP_PROFIT            ?  TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc)                :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Liefert die Beschreibung der String-Eigenschaft des Ereignisses  |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property)
  {
   return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\"";
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Ereignis-Status                          |
//+------------------------------------------------------------------+
string CEvent::StatusDescription(void) const
  {
   ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);
   return
     (
      status==EVENT_STATUS_MARKET_PENDING    ?  TextByLanguage("Установлен отложенный ордер","Pending order placed") :
      status==EVENT_STATUS_MARKET_POSITION   ?  TextByLanguage("Открыта позиция","Position opened")                 :
      status==EVENT_STATUS_HISTORY_PENDING   ?  TextByLanguage("Удален отложенный ордер","Pending order removed")    :
      status==EVENT_STATUS_HISTORY_POSITION  ?  TextByLanguage("Закрыта позиция","Position closed")                  :
      status==EVENT_STATUS_BALANCE           ?  TextByLanguage("Балансная операция","Balance operation")             :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Handelsereignisses                       |
//+------------------------------------------------------------------+
string CEvent::TypeEventDescription(void) const
  {
   ENUM_TRADE_EVENT event=this.TypeEvent();
   return
     (
      event==TRADE_EVENT_PENDING_ORDER_PLASED            ?  TextByLanguage("Отложенный ордер установлен","Pending order placed")                                  :
      event==TRADE_EVENT_PENDING_ORDER_REMOVED           ?  TextByLanguage("Отложенный ордер удалён","Pending order removed")                                     :
      event==TRADE_EVENT_ACCOUNT_CREDIT                  ?  TextByLanguage("Начисление кредита","Credit")                                                         :
      event==TRADE_EVENT_ACCOUNT_CHARGE                  ?  TextByLanguage("Дополнительные сборы","Additional charge")                                            :
      event==TRADE_EVENT_ACCOUNT_CORRECTION              ?  TextByLanguage("Корректирующая запись","Correction")                                                  :
      event==TRADE_EVENT_ACCOUNT_BONUS                   ?  TextByLanguage("Перечисление бонусов","Bonus")                                                        :
      event==TRADE_EVENT_ACCOUNT_COMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                     :
      event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                           :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")           :
      event==TRADE_EVENT_ACCOUNT_INTEREST                ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                          :
      event==TRADE_EVENT_BUY_CANCELLED                   ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                       :
      event==TRADE_EVENT_SELL_CANCELLED                  ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                      :
      event==TRADE_EVENT_DIVIDENT                        ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                          :
      event==TRADE_EVENT_DIVIDENT_FRANKED                ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations")    :
      event==TRADE_EVENT_TAX                             ?  TextByLanguage("Начисление налога","Tax charges")                                                     :
      event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL          ?  TextByLanguage("Пополнение средств на балансе","Balance refill")                                      :
      event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL      ?  TextByLanguage("Снятие средств с баланса","Withdrawals")                                              :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED         ?  TextByLanguage("Отложенный ордер активирован ценой","Pending order activated")                        :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ?  TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially")     :
      event==TRADE_EVENT_POSITION_OPENED                 ?  TextByLanguage("Позиция открыта","Position opened")                                                  :
      event==TRADE_EVENT_POSITION_OPENED_PARTIAL         ?  TextByLanguage("Позиция открыта частично","Position opened partially")                               :
      event==TRADE_EVENT_POSITION_CLOSED                 ?  TextByLanguage("Позиция закрыта","Position closed")                                                   :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL         ?  TextByLanguage("Позиция закрыта частично","Position closed partially")                                :
      event==TRADE_EVENT_POSITION_CLOSED_BY_POS          ?  TextByLanguage("Позиция закрыта встречной","Position closed by opposite position")                    :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS  ?  TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") :
      event==TRADE_EVENT_POSITION_CLOSED_BY_SL           ?  TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss")                           :
      event==TRADE_EVENT_POSITION_CLOSED_BY_TP           ?  TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit")                       :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL   ?  TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss")        :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP   ?  TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit")    :
      event==TRADE_EVENT_POSITION_REVERSED               ?  TextByLanguage("Разворот позиции","Position reversal")                                                :
      event==TRADE_EVENT_POSITION_VOLUME_ADD             ?  TextByLanguage("Добавлен объём к позиции","Added volume to position")                                 :
      TextByLanguage("Нет торгового события","No trade event")
     );   
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens von Order/Deal/Position                      |
//+------------------------------------------------------------------+
string CEvent::TypeOrderDescription(void) const
  {
   ENUM_EVENT_STATUS status=this.Status();
   return
     (
      status==EVENT_STATUS_MARKET_PENDING  || status==EVENT_STATUS_HISTORY_PENDING  ?  OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT))      :
      status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ?  PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) :
      status==EVENT_STATUS_BALANCE  ?  DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT))  :  "Unknown"
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des übergeordneten Auftrages                 |
//+------------------------------------------------------------------+
string CEvent::TypeOrderBasedDescription(void) const
  {
   return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
  }
//+------------------------------------------------------------------+
//| Rückgabe des Positionsnamen                                      |
//+------------------------------------------------------------------+
string CEvent::TypePositionDescription(void) const
  {
   ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
   return PositionTypeDescription(type);
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Grundes von Order/Deal/Position          |
//+------------------------------------------------------------------+
string CEvent::ReasonDescription(void) const
  {
   ENUM_EVENT_REASON reason=this.Reason();
   return 
     (
      reason==EVENT_REASON_ACTIVATED_PENDING                ?  TextByLanguage("Активирован отложенный ордер","Pending order activated")                           :
      reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY      ?  TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered")    :
      reason==EVENT_REASON_CANCEL                           ?  TextByLanguage("Отмена","Canceled")                                                                :
      reason==EVENT_REASON_EXPIRED                          ?  TextByLanguage("Истёк срок действия","Expired")                                                    :
      reason==EVENT_REASON_DONE                             ?  TextByLanguage("Запрос выполнен полностью","Request fully executed")                            :
      reason==EVENT_REASON_DONE_PARTIALLY                   ?  TextByLanguage("Запрос выполнен частично","Request partially executed")                         :
      reason==EVENT_REASON_DONE_SL                          ?  TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered")                               :
      reason==EVENT_REASON_DONE_SL_PARTIALLY                ?  TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered")             :
      reason==EVENT_REASON_DONE_TP                          ?  TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered")                           :
      reason==EVENT_REASON_DONE_TP_PARTIALLY                ?  TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered")         :
      reason==EVENT_REASON_DONE_BY_POS                      ?  TextByLanguage("Закрытие встречной позицией","Closed by opposite position")                        :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS            ?  TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position")    :
      reason==EVENT_REASON_DONE_BY_POS_PARTIALLY            ?  TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY  ?  TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position")  :
      reason==EVENT_REASON_BALANCE_REFILL                   ?  TextByLanguage("Пополнение баланса","Balance refill")                                              :
      reason==EVENT_REASON_BALANCE_WITHDRAWAL               ?  TextByLanguage("Снятие средств с баланса","Withdrawals from balance")                          :
      reason==EVENT_REASON_ACCOUNT_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit")                                                      :
      reason==EVENT_REASON_ACCOUNT_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Additional charge")                                         :
      reason==EVENT_REASON_ACCOUNT_CORRECTION               ?  TextByLanguage("Корректирующая запись","Correction")                                               :
      reason==EVENT_REASON_ACCOUNT_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonus")                                                     :
      reason==EVENT_REASON_ACCOUNT_COMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                  :
      reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                        :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")        :
      reason==EVENT_REASON_ACCOUNT_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                       :
      reason==EVENT_REASON_BUY_CANCELLED                    ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                    :
      reason==EVENT_REASON_SELL_CANCELLED                   ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                   :
      reason==EVENT_REASON_DIVIDENT                         ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                       :
      reason==EVENT_REASON_DIVIDENT_FRANKED                 ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      reason==EVENT_REASON_TAX                              ?  TextByLanguage("Начисление налога","Tax charges")                                                  :
      EnumToString(reason)
     );
  }
//+------------------------------------------------------------------+
//| Anzeige der Ereigniseigenschaften im Journal                     |
//+------------------------------------------------------------------+
void CEvent::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Die Logik all dieser Methoden ist ähnlich wie die der bereits beschriebenen Auftragsdatenausgabemethoden. Deshalb werden wir uns nicht auf sie konzentrieren, da hier alles ganz einfach und visuell verständlich ist.

Die vollständige Liste der Eventklassen:

//+------------------------------------------------------------------+
//|                                                        Event.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
#property strict    // Notwendig für mql4
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "\..\..\Services\DELib.mqh"
#include "..\..\Collections\HistoryCollection.mqh"
#include "..\..\Collections\MarketCollection.mqh"
//+------------------------------------------------------------------+
//| Abstrakte Ereignisklasse                                         |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // Ereigniscode
//--- Rückgabe des Arrayindex des Ereignisses mit den (1) Double- und (2) String-Eigenschaften
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // Handelsereignis
   long              m_chart_id;                                     // Chart-ID des Steuerprogramms
   int               m_digits_acc;                                   // Dezimalstellen der Kontowährung
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // Integer-Eigenschaften des Ereignisses
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // Double-Eigenschaften des Ereignisses
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // String-Eigenschaften des Ereignisses
//--- Rückgabe des Vorhandenseins des Flags des Handelsereignisses
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;            }

   //--- 'Protected' Konstruktor
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- Standardmäßiger Konstruktor
                     CEvent(void){;}
 
//--- Setzen der (1) Integer-, (2) Double- und (3) String-Eigenschaften des Ereignisses
   void              SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;    }
//--- Rückgabe von (1) Integer-, (2) Double- und (3) String-Eigenschaften aus dem Array der Eigenschaften
   long              GetProperty(ENUM_EVENT_PROP_INTEGER property)      const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_EVENT_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_EVENT_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];   }

//--- Rückgabe des Flags des Ereignisses, das die Eigenschaften unterstützt
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property)         { return true; }

//--- Setzen der Chart-ID des Steuerprogramms
   void              SetChartID(const long id)                                { this.m_chart_id=id;                                    }
//--- Dekodieren des Ereigniscodes und setzen des Handelsereignisses, (2) Rückgabe des Handelsereignisses
   void              SetTypeEvent(void);
   ENUM_TRADE_EVENT  TradeEvent(void)                                   const { return this.m_trade_event;                             }
//--- Senden des Ereignisses an das Chart (Implementation in der abgeleiteten Klasse)
   virtual void      SendEvent(void) {;}

//--- Vergleichen der CEvent-Objekte nach der angegebenen Eigenschaft (für das Sorteiren der Liste nach der angegebenen Eigenschaft des Ereignisobjektes)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//---- Vergleich von CEvent-Objekten nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen)
   bool              IsEqual(CEvent* compared_event);
//+------------------------------------------------------------------+
//| Methode für einen vereinfachten Zugriff auf die Eigenschaften    |
//+------------------------------------------------------------------+
//--- Rückgabe von (1) Ereignis-Typ, (2) Ereignis-Zeit in Millisekunden, (3) Ereignis-Status, (4) Ereignis-Grund, (5) Dealtyp, (6) Deal-Ticket, 
//--- (7) Auftragstyp des dadurch geöffneten Deal-Ereignisses, (8) Auftragstyp der Positionseröffnung, (9) letztes Auftragsticket der Position, 
//--- (10) erstes Auftragsticket der Position, (11) Positions-ID, (12) ID der entgegengesetzten Position, (13) Magicnummer, (14) Eröffnungszeit der Position

   ENUM_TRADE_EVENT  TypeEvent(void)                                    const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT);     }
   long              TimeEvent(void)                                    const { return this.GetProperty(EVENT_PROP_TIME_EVENT);                       }
   ENUM_EVENT_STATUS Status(void)                                       const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);  }
   ENUM_EVENT_REASON Reason(void)                                       const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT);  }
   long              TypeDeal(void)                                     const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);                  }
   long              TicketDeal(void)                                   const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT);                }
   long              TypeOrderEvent(void)                               const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT);                 }
   long              TypeOrderPosition(void)                            const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION);              }
   long              TicketOrderEvent(void)                             const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT);               }
   long              TicketOrderPosition(void)                          const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION);            }
   long              PositionID(void)                                   const { return this.GetProperty(EVENT_PROP_POSITION_ID);                      }
   long              PositionByID(void)                                 const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID);                   }
   long              Magic(void)                                        const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER);                      }
   long              TimePosition(void)                                 const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION);              }
   
//--- Rückgabe von (1) Preis bei den ein Ereignis auftritt, (2) Eröffnungspreis, (3) Schließpreis,
//--- (4) StopLoss, (5) TakeProfit, (6) Gewinn, (7) verlangtes Volumen, (8), ausgeführtes Volumen, (9) verbliebenes Volumen
   double            PriceEvent(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_EVENT);                      }
   double            PriceOpen(void)                                    const { return this.GetProperty(EVENT_PROP_PRICE_OPEN);                       }
   double            PriceClose(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE);                      }
   double            PriceStopLoss(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_SL);                         }
   double            PriceTakeProfit(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_TP);                         }
   double            Profit(void)                                       const { return this.GetProperty(EVENT_PROP_PROFIT);                           }
   double            VolumeInitial(void)                                const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL);                   }
   double            VolumeExecuted(void)                               const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED);                  }
   double            VolumeCurrent(void)                                const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT);                   }
   
//--- Rückgabe eines Symbols
   string            Symbol(void)                                       const { return this.GetProperty(EVENT_PROP_SYMBOL);                           }
   
//+------------------------------------------------------------------+
//| Beschreibung der Objekteigenschaften des Auftrags                |
//+------------------------------------------------------------------+
//--- Rückgabe der Beschreibung der (1) Integer-, (2) Double- und (3) String-Eigenschaft des Auftrages
   string            GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_STRING property);
//--- Rückgabe von (1) Status und (2) Typ des Ereignisses
   string            StatusDescription(void)          const;
   string            TypeEventDescription(void)       const;
//--- Rückgabe von (1) Order/Deal/Position, (2) übergeordnete Order, (3) Position
   string            TypeOrderDescription(void)       const;
   string            TypeOrderBasedDescription(void)  const;
   string            TypePositionDescription(void)    const;
//--- Rückgabe des Namens des Grundes von Order/Deal/Position
   string            ReasonDescription(void)          const;

//--- Anzeigen (1) Beschreibung der Auftragseigenschaften (full_prop=true - alle Eigenschaften, false - nur die unterstützten),
//--- (2) kurze Ereignisnachrichten (implementiert in abgeleiteten Klassen) im Journal
   void              Print(const bool full_prop=false);
   virtual void      PrintShort(void) {;}
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+
//| Vergleich einer bestimmten Eigenschaft von CEvent-Objekten       |
//+------------------------------------------------------------------+
int CEvent::Compare(const CObject *node,const int mode=0) const
  {
   const CEvent *event_compared=node;
//--- Vergleich der Integer-Eigenschaften von zwei Ereignissen
   if(mode<EVENT_PROP_INTEGER_TOTAL)
     {
      long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- Vergleich der Double-Eigenschaften von zwei Ereignisses
   if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL)
     {
      double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- Vergleich der String-Eigenschaften von zwei Ereignissen
   else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL)
     {
      string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+
//| Vergleichen von COrder-Objekte nach allen Eigenschaften          |
//+------------------------------------------------------------------+
bool CEvent::IsEqual(CEvent *compared_event)
  {
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//| Dekodieren des Ereigniscodes und setzen des Handelsereignisses   |
//+------------------------------------------------------------------+
void CEvent::SetTypeEvent(void)
  {
//--- Pending-Order platziert (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Pending-Order entfernt (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Positionseröffnung (Prüfen mehrerer Flags im Ereigniscode)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED))
     {
      //--- Wenn die Pending-Order durch den Preis aktiviert wurde
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Pending-Order aktiviert" oder "Pending-Order teilweise aktiviert""
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Prüfen des Flags für ein teilweises Eröffnen und setzen des Handelsereignisses von "Position eröffnet" oder "Position teilweise eröffnet"
      this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL);
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Position geschlossen (Prüfen mehrerer Flags im Ereigniscode)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED))
     {
      //--- wenn eine Position durch StopLoss geschlossen wurde
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch StopLoss" oder "Position teilweise geschlossen durch StopLoss"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn die Position durch TakeProfit geschlossen wurde
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch TakeProfit" oder "Position teilweise geschlossen durch TakeProfit"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn eine Position durch eine Gegenposition geschlossen wurde
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS))
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch eine Gegenposition" oder "Position teilweise geschlossen durch eine Gegenposition"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Wenn eine Position geschlossen wurde
      else
        {
         //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen" oder "Position teilweise geschlossen"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
//--- Saldooperation auf dem Konto (Klärung des Ereignisses durch den Dealtyp)
   if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE)
     {
      //--- Initialisierung des Handelsereignisses
      this.m_trade_event=TRADE_EVENT_NO_EVENT;
      //--- Nehmen des Dealtyps
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);
      //--- Wenn der Deal eine Saldenoperationen ist
      if(deal_type==DEAL_TYPE_BALANCE)
        {
        //--- Prüfen des Deals-Gewinns und setzen des Ereignisses (Gelder zu- oder abbuchen)
         this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL);
        }
      //--- Buchungstyp des verbliebenen Saldos passt zur Enumeration ENUM_DEAL_TYPE beginnend mit DEAL_TYPE_CREDIT
      else if(deal_type>DEAL_TYPE_BALANCE)
        {
        //--- Setzen des Ereignisses
         this.m_trade_event=(ENUM_TRADE_EVENT)deal_type;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
  }
//+------------------------------------------------------------------+
//| Liefert die Beschreibung der Integer-Eigenschaft des Ereignisses |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property)
  {
   return
     (
      property==EVENT_PROP_TYPE_EVENT              ?  TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription()                                                       :
      property==EVENT_PROP_TIME_EVENT              ?  TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property))                                    :
      property==EVENT_PROP_STATUS_EVENT            ?  TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\""                                             :
      property==EVENT_PROP_REASON_EVENT            ?  TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription()                                                   :
      property==EVENT_PROP_TYPE_DEAL_EVENT         ?  TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property))                     :
      property==EVENT_PROP_TICKET_DEAL_EVENT       ?  TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property)                                              :
      property==EVENT_PROP_TYPE_ORDER_EVENT        ?  TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property))    :
      property==EVENT_PROP_TYPE_ORDER_POSITION     ?  TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) :
      property==EVENT_PROP_TICKET_ORDER_POSITION   ?  TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property)              :
      property==EVENT_PROP_TICKET_ORDER_EVENT      ?  TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property)                               :
      property==EVENT_PROP_POSITION_ID             ?  TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property)                                       :
      property==EVENT_PROP_POSITION_BY_ID          ?  TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+" #"+(string)this.GetProperty(property)                  :
      property==EVENT_PROP_MAGIC_ORDER             ?  TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property)                                           :
      property==EVENT_PROP_TIME_ORDER_POSITION     ?  TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property))                  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe der Beschreibung der Double-Eigenschaft des Ereignisses |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL));
   return
     (
      property==EVENT_PROP_PRICE_EVENT       ?  TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) :
      property==EVENT_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg)                    :
      property==EVENT_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg)                   :
      property==EVENT_PROP_PRICE_SL          ?  TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg)                :
      property==EVENT_PROP_PRICE_TP          ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg)            :
      property==EVENT_PROP_VOLUME_INITIAL    ?  TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl)             :
      property==EVENT_PROP_VOLUME_EXECUTED   ?  TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl)          :
      property==EVENT_PROP_VOLUME_CURRENT    ?  TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl)          :
      property==EVENT_PROP_PROFIT            ?  TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc)                :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Liefert die Beschreibung der String-Eigenschaft des Ereignisses  |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property)
  {
   return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\"";
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Ereignisstatus                           |
//+------------------------------------------------------------------+
string CEvent::StatusDescription(void) const
  {
   ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);
   return
     (
      status==EVENT_STATUS_MARKET_PENDING    ?  TextByLanguage("Установлен отложенный ордер","Pending order placed") :
      status==EVENT_STATUS_MARKET_POSITION   ?  TextByLanguage("Открыта позиция","Position opened")                 :
      status==EVENT_STATUS_HISTORY_PENDING   ?  TextByLanguage("Удален отложенный ордер","Pending order removed")    :
      status==EVENT_STATUS_HISTORY_POSITION  ?  TextByLanguage("Закрыта позиция","Position closed")                  :
      status==EVENT_STATUS_BALANCE           ?  TextByLanguage("Балансная операция","Balance operation")             :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Handelsereignisses                       |
//+------------------------------------------------------------------+
string CEvent::TypeEventDescription(void) const
  {
   ENUM_TRADE_EVENT event=this.TypeEvent();
   return
     (
      event==TRADE_EVENT_PENDING_ORDER_PLASED            ?  TextByLanguage("Отложенный ордер установлен","Pending order placed")                                  :
      event==TRADE_EVENT_PENDING_ORDER_REMOVED           ?  TextByLanguage("Отложенный ордер удалён","Pending order removed")                                     :
      event==TRADE_EVENT_ACCOUNT_CREDIT                  ?  TextByLanguage("Начисление кредита","Credit")                                                         :
      event==TRADE_EVENT_ACCOUNT_CHARGE                  ?  TextByLanguage("Дополнительные сборы","Additional charge")                                            :
      event==TRADE_EVENT_ACCOUNT_CORRECTION              ?  TextByLanguage("Корректирующая запись","Correction")                                                  :
      event==TRADE_EVENT_ACCOUNT_BONUS                   ?  TextByLanguage("Перечисление бонусов","Bonus")                                                        :
      event==TRADE_EVENT_ACCOUNT_COMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                     :
      event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                           :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")           :
      event==TRADE_EVENT_ACCOUNT_INTEREST                ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                          :
      event==TRADE_EVENT_BUY_CANCELLED                   ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                       :
      event==TRADE_EVENT_SELL_CANCELLED                  ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                      :
      event==TRADE_EVENT_DIVIDENT                        ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                          :
      event==TRADE_EVENT_DIVIDENT_FRANKED                ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations")    :
      event==TRADE_EVENT_TAX                             ?  TextByLanguage("Начисление налога","Tax charges")                                                     :
      event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL          ?  TextByLanguage("Пополнение средств на балансе","Balance refill")                                      :
      event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL      ?  TextByLanguage("Снятие средств с баланса","Withdrawals")                                              :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED         ?  TextByLanguage("Отложенный ордер активирован ценой","Pending order activated")                        :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ?  TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially")     :
      event==TRADE_EVENT_POSITION_OPENED                 ?  TextByLanguage("Позиция открыта","Position opened")                                                  :
      event==TRADE_EVENT_POSITION_OPENED_PARTIAL         ?  TextByLanguage("Позиция открыта частично","Position opened partially")                               :
      event==TRADE_EVENT_POSITION_CLOSED                 ?  TextByLanguage("Позиция закрыта","Position closed")                                                   :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL         ?  TextByLanguage("Позиция закрыта частично","Position closed partially")                                :
      event==TRADE_EVENT_POSITION_CLOSED_BY_POS          ?  TextByLanguage("Позиция закрыта встречной","Position closed by opposite position")                    :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS  ?  TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") :
      event==TRADE_EVENT_POSITION_CLOSED_BY_SL           ?  TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss")                           :
      event==TRADE_EVENT_POSITION_CLOSED_BY_TP           ?  TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit")                       :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL   ?  TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss")        :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP   ?  TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit")    :
      event==TRADE_EVENT_POSITION_REVERSED               ?  TextByLanguage("Разворот позиции","Position reversal")                                                :
      event==TRADE_EVENT_POSITION_VOLUME_ADD             ?  TextByLanguage("Добавлен объём к позиции","Added volume to position")                                 :
      TextByLanguage("Нет торгового события","No trade event")
     );   
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens von Order/Deal/Position                      |
//+------------------------------------------------------------------+
string CEvent::TypeOrderDescription(void) const
  {
   ENUM_EVENT_STATUS status=this.Status();
   return
     (
      status==EVENT_STATUS_MARKET_PENDING  || status==EVENT_STATUS_HISTORY_PENDING  ?  OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT))      :
      status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ?  PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) :
      status==EVENT_STATUS_BALANCE  ?  DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT))  :  "Unknown"
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des übergeordneten Auftrages                 |
//+------------------------------------------------------------------+
string CEvent::TypeOrderBasedDescription(void) const
  {
   return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
  }
//+------------------------------------------------------------------+
//| Rückgabe des Positionsnamen                                      |
//+------------------------------------------------------------------+
string CEvent::TypePositionDescription(void) const
  {
   ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
   return PositionTypeDescription(type);
  }
//+------------------------------------------------------------------+
//| Rückgabe des Namens des Grundes von Order/Deal/Position          |
//+------------------------------------------------------------------+
string CEvent::ReasonDescription(void) const
  {
   ENUM_EVENT_REASON reason=this.Reason();
   return 
     (
      reason==EVENT_REASON_ACTIVATED_PENDING                ?  TextByLanguage("Активирован отложенный ордер","Pending order activated")                           :
      reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY      ?  TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered")    :
      reason==EVENT_REASON_CANCEL                           ?  TextByLanguage("Отмена","Canceled")                                                                :
      reason==EVENT_REASON_EXPIRED                          ?  TextByLanguage("Истёк срок действия","Expired")                                                    :
      reason==EVENT_REASON_DONE                             ?  TextByLanguage("Запрос выполнен полностью","Request fully executed")                            :
      reason==EVENT_REASON_DONE_PARTIALLY                   ?  TextByLanguage("Запрос выполнен частично","Request partially executed")                         :
      reason==EVENT_REASON_DONE_SL                          ?  TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered")                               :
      reason==EVENT_REASON_DONE_SL_PARTIALLY                ?  TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered")             :
      reason==EVENT_REASON_DONE_TP                          ?  TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered")                           :
      reason==EVENT_REASON_DONE_TP_PARTIALLY                ?  TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered")         :
      reason==EVENT_REASON_DONE_BY_POS                      ?  TextByLanguage("Закрытие встречной позицией","Closed by opposite position")                        :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS            ?  TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position")    :
      reason==EVENT_REASON_DONE_BY_POS_PARTIALLY            ?  TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY  ?  TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position")  :
      reason==EVENT_REASON_BALANCE_REFILL                   ?  TextByLanguage("Пополнение баланса","Balance refill")                                              :
      reason==EVENT_REASON_BALANCE_WITHDRAWAL               ?  TextByLanguage("Снятие средств с баланса","Withdrawals from balance")                          :
      reason==EVENT_REASON_ACCOUNT_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit")                                                      :
      reason==EVENT_REASON_ACCOUNT_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Additional charge")                                         :
      reason==EVENT_REASON_ACCOUNT_CORRECTION               ?  TextByLanguage("Корректирующая запись","Correction")                                               :
      reason==EVENT_REASON_ACCOUNT_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonus")                                                     :
      reason==EVENT_REASON_ACCOUNT_COMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                  :
      reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                        :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")        :
      reason==EVENT_REASON_ACCOUNT_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                       :
      reason==EVENT_REASON_BUY_CANCELLED                    ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                    :
      reason==EVENT_REASON_SELL_CANCELLED                   ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                   :
      reason==EVENT_REASON_DIVIDENT                         ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                       :
      reason==EVENT_REASON_DIVIDENT_FRANKED                 ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      reason==EVENT_REASON_TAX                              ?  TextByLanguage("Начисление налога","Tax charges")                                                  :
      EnumToString(reason)
     );
  }
//+------------------------------------------------------------------+
//| Anzeige der Ereigniseigenschaften im Journal                     |
//+------------------------------------------------------------------+
void CEvent::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Die Klasse des abstrakten Basisereignisses ist fertig. Jetzt müssen wir fünf abgeleitete Klassen erstellen, die ein Ereignis sind und ihren Typ angeben: Platzieren einer Pending-Order, Löschen einer Pending-Order, Öffnen einer Position, Schließen einer Position und eine Saldooperation.

Erstellen wir eine abgeleitete Klasse mit dem Ereignisstatus "Pending-Order platzieren".

Im Ordner Events Library erstellen wir eine neue Datei der Klasse CEventOrderPlased EventOrderPlased.mqh mit der Basisklasse CEvent und fügen alle notwendigen Verbindungen und Methoden hinzu:

//+------------------------------------------------------------------+
//|                                             EventOrderPlased.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Event.mqh"
//+------------------------------------------------------------------+
//| Platzieren des Ereignisses einer Pending-Order                   |
//+------------------------------------------------------------------+
class CEventOrderPlased : public CEvent
  {
public:
//--- Konstructor
                     CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {}
//--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Übergeben Sie den Ereigniscode und das Ticket eines Auftrags oder Deals, das das Ereignis ausgelöst hat, an den Klassenkonstruktor und senden Sie den Ereignisstatus "Platzieren einer Pending-Order". (EVENT_STATUS_MARKET_PENDING), den Ereigniscode und ein Auftrags- oder Dealticket an die übergeordnete Klasse in der Initialisierungsliste:

CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {}

Wir haben die Methoden, die die Flags eines Objekts zurückgeben, das bestimmte Eigenschaften von SupportProperty() unterstützt, bereits im ersten Teil der Bibliotheksbeschreibung beschrieben. Hier ist alles gleich:

//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   if(property==EVENT_PROP_TYPE_DEAL_EVENT         ||
      property==EVENT_PROP_TICKET_DEAL_EVENT       ||
      property==EVENT_PROP_TYPE_ORDER_POSITION     ||
      property==EVENT_PROP_TICKET_ORDER_POSITION   ||
      property==EVENT_PROP_POSITION_ID             ||
      property==EVENT_PROP_POSITION_BY_ID          ||
      property==EVENT_PROP_TIME_ORDER_POSITION
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   if(property==EVENT_PROP_PRICE_CLOSE             ||
      property==EVENT_PROP_PROFIT
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Das übergeordnete Ereignisobjekt CEvent verfügt über die Methode Print(), die vollständige Daten über alle unterstützten Ereignisobjekteigenschaften anzeigt, und die virtuelle Methode PrintShort(), die es ermöglicht, ausreichende Daten über das Ereignis im Terminaljournal in zwei Zeilen anzuzeigen.
Die Implementierung der Methode PrintShort() in jeder abgeleiteten Klasse des Basis-Ereignisobjekts ist individuell, da sich Ereignisse auch in ihrer Herkunft unterscheiden:

//+------------------------------------------------------------------+
//| Eine kurze Ereignismeldung im Journal anzeigen                   |
//+------------------------------------------------------------------+
void CEventOrderPlased::PrintShort(void)
  {
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol()));
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent();
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS));
   string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic;
   ::Print(txt);
  }
//+------------------------------------------------------------------+

Hier machen wir Folgendes:

Die Methode, ein nutzerdefiniertes Ereignis an das Diagramm zu senden, ist recht einfach:

//+------------------------------------------------------------------+
//| Senden des Ereignisses an das Chart                              |
//+------------------------------------------------------------------+
void CEventOrderPlased::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol());
  }
//+------------------------------------------------------------------+

Zuerst wird eine kurze Nachricht über das Ereignis im Journal angezeigt, dann wird das nutzerdefinierte Ereignis EventChartCustom() an die Chart-ID m_chart_id der Basis-Event-Klasse CEvent gesendet.
Senden des Ereignisses m_trade_event an die Event ID,
Auftragsticket — zum long Parametertyp,
Auftragspreis — zum Double
Parametertyp,
Auftragssymbol — zum string Parametertyp.

Die Klasse für die Anzeige von Nachrichten, die es n ermöglicht, Nachrichtenebenen für die Anzeige nur notwendiger Daten im Journal festzulegen, soll in Zukunft entwickelt werden. Im aktuellen Entwicklungsstadium der Bibliothek werden standardmäßig alle Meldungen angezeigt.

Betrachten wir die vollständige Liste der anderen Ereignisklassen.
"Entfernen von Pending-Orders":

//+------------------------------------------------------------------+
//|                                            EventOrderRemoved.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Event.mqh"
//+------------------------------------------------------------------+
//| Ereignis des Platzierens einer Pending-Order                     |
//+------------------------------------------------------------------+
class CEventOrderRemoved : public CEvent
  {
public:
//--- Konstructor
                     CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket) {}
//--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   if(property==EVENT_PROP_TYPE_DEAL_EVENT         ||
      property==EVENT_PROP_TICKET_DEAL_EVENT       ||
      property==EVENT_PROP_TYPE_ORDER_POSITION     ||
      property==EVENT_PROP_TICKET_ORDER_POSITION   ||
      property==EVENT_PROP_TIME_ORDER_POSITION
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   return(property==EVENT_PROP_PROFIT ? false : true);
  }
//+------------------------------------------------------------------+
//| Eine kurze Ereignismeldung im Journal anzeigen                   |
//+------------------------------------------------------------------+
void CEventOrderRemoved::PrintShort(void)
  {
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol()));
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent();
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS));
   string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic;
   ::Print(txt);
  }
//+------------------------------------------------------------------+
//| Senden des Ereignisses an das Chart                              |
//+------------------------------------------------------------------+
void CEventOrderRemoved::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol());
  }
//+------------------------------------------------------------------+

Ereignisklasse "Position eröffnen":

//+------------------------------------------------------------------+
//|                                            EventPositionOpen.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Event.mqh"
//+------------------------------------------------------------------+
//| Ereignis einer Positionseröffnung                                |
//+------------------------------------------------------------------+
class CEventPositionOpen : public CEvent
  {
public:
//--- Konstructor
                     CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {}
//--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   return(property==EVENT_PROP_POSITION_BY_ID ? false : true);
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true' bei Unterstützung der übergebenen            |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   if(property==EVENT_PROP_PRICE_CLOSE ||
      property==EVENT_PROP_PROFIT
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Eine kurze Ereignismeldung im Journal anzeigen                   |
//+------------------------------------------------------------------+
void CEventPositionOpen::PrintShort(void)
  {
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string order=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? " #"+(string)this.TicketOrderPosition() : "");
   string activated=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? TextByLanguage(" активацией ордера "," by ")+this.TypeOrderBasedDescription() : "");
   string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : "");
   string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol()));
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string type=this.TypePositionDescription()+" #"+(string)this.PositionID();
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS));
   string txt=head+this.Symbol()+" "+vol+" "+type+activated+order+price+sl+tp+magic;
   ::Print(txt);
  }
//+------------------------------------------------------------------+
//| Senden des Ereignisses an das Chart                              |
//+------------------------------------------------------------------+
void CEventPositionOpen::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol());
  }
//+------------------------------------------------------------------+

Ereignisklasse "Position schließen":

//+------------------------------------------------------------------+
//|                                           EventPositionClose.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Event.mqh"
//+------------------------------------------------------------------+
//| Ereignis einer Positionseröffnung                                |
//+------------------------------------------------------------------+
class CEventPositionClose : public CEvent
  {
public:
//--- Konstructor
                     CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {}
//--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Anzeige einer kurzen Nachricht über das Ereignis im Journal      |
//+------------------------------------------------------------------+
void CEventPositionClose::PrintShort(void)
  {
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string opposite=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS) ? " by "+this.TypeOrderDescription()+" #"+(string)this.PositionByID() : "");
   string vol=::DoubleToString(this.VolumeExecuted(),DigitsLots(this.Symbol()));
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string type=this.TypePositionDescription()+" #"+(string)this.PositionID()+opposite;
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceClose(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS));
   string profit=TextByLanguage(", профит: ",", profit: ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY);
   string txt=head+this.Symbol()+" "+vol+" "+type+price+magic+profit;
   ::Print(txt);
  }
//+------------------------------------------------------------------+
//| Senden des Ereignisses an das Chart                              |
//+------------------------------------------------------------------+
void CEventPositionClose::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol());
  }
//+------------------------------------------------------------------+

Ereignisklasse "Saldooperation":

//+------------------------------------------------------------------+
//|                                        EventBalanceOperation.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Event.mqh"
//+------------------------------------------------------------------+
//| Ereignis einer Positionseröffnung                                |
//+------------------------------------------------------------------+
class CEventBalanceOperation : public CEvent
  {
public:
//--- Konstructor
                     CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket) {}
//--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property);
//--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Integer-Eigenschaft unterstützt, sonst 'false'                   |
//+------------------------------------------------------------------+
bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   if(property==EVENT_PROP_TYPE_ORDER_EVENT        ||
      property==EVENT_PROP_TYPE_ORDER_POSITION     ||
      property==EVENT_PROP_TICKET_ORDER_EVENT      ||
      property==EVENT_PROP_TICKET_ORDER_POSITION   ||
      property==EVENT_PROP_POSITION_ID             ||
      property==EVENT_PROP_POSITION_BY_ID          ||
      property==EVENT_PROP_POSITION_ID             ||
      property==EVENT_PROP_MAGIC_ORDER             ||
      property==EVENT_PROP_TIME_ORDER_POSITION
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| Double-Eigenschaft, sonst 'false'                                |
//+------------------------------------------------------------------+
bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   return(property==EVENT_PROP_PROFIT ? true : false);
  }
//+------------------------------------------------------------------+
//| Rückgabe von 'true', wenn das Ereignis die übergebene            |
//| String-Eigenschaften, sonst 'false'                              |
//+------------------------------------------------------------------+
bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_STRING property)
  {
   return false;
  }
//+------------------------------------------------------------------+
//| Anzeige einer kurzen Nachricht über das Ereignis im Journal      |
//+------------------------------------------------------------------+
void CEventBalanceOperation::PrintShort(void)
  {
   string head="- "+this.StatusDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   ::Print(head+this.TypeEventDescription()+": "+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY));
  }
//+------------------------------------------------------------------+
//| Senden des Ereignisses an das Chart                              |
//+------------------------------------------------------------------+
void CEventBalanceOperation::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TypeEvent(),this.Profit(),::AccountInfoString(ACCOUNT_CURRENCY));
  }
//+------------------------------------------------------------------+

Wie wir aus den Listen ersehen können, unterscheiden sich die Klassen nur durch die Anzahl der unterstützten Eigenschaften, den Status, der an den übergeordneten Klassenkonstruktor gesendet wird, und die Methoden PrintShort(), da jedes Ereignis seine eigenen Funktionen hat, die für das Journal bestimmt sind. All dies kann aus den Listen der Methoden verstanden werden, und Sie können sie selbst analysieren, so dass es keinen Sinn macht, sich mit ihnen zu beschäftigen. Kommen wir zur Entwicklung der Klasse der Kollektion der Ereignisse.

Kollektion von Handelsereignissen

Im vierten Teil der Bibliotheksbeschreibung haben wir getestet, um Kontoereignisse und deren Anzeige im Journal und im EA zu definieren. Allerdings konnten wir nur das letzte Ereignis verfolgen. Außerdem befand sich die gesamte Funktionalität in der Basisklasse der Bibliothek CEngine.
Die richtige Entscheidung ist, alles in eine eigene Klasse zu stellen und alle darin auftretenden Ereignisse zu verarbeiten.

Um dies zu erreichen, habe ich Ereignisobjekte entwickelt. Jetzt müssen wir die Klasse dazu bringen, eine beliebige Anzahl von gleichzeitig aufgetretenen Ereignissen zu behandeln. Schließlich kann es zu einer Situation kommen, in der Pending-Orders entfernt oder platziert werden oder mehrere Positionen gleichzeitig in einer einzigen Schleife geschlossen werden.
Das Prinzip, das wir bereits in solchen Situationen getestet haben, würde uns nur das jüngste von mehreren auf einmal durchgeführten Ereignissen geben. Ich denke, das ist falsch. Lassen Sie uns daher die Klasse, die alle Ereignisse, die auf einmal stattgefunden haben, speichert, in die Liste der Ereigniskollektionen aufnehmen. Außerdem wird es in Zukunft möglich sein, mit den Methoden dieser Klassen durch die Historie des Kontos zu gehen und alles wiederherzustellen, was seit seiner Eröffnung auf ihm passiert ist.

Erstellen wir in den DoEasy\Collections die neue Datei der Klasse CEventsCollection namens EventsCollection.mqh. Die Klasse CListObj sollte zur Basisklasse gemacht werden.

Füllen Sie die neu erstellte Klassenvorlage mit allen notwendigen Einbindungen, Mitgliedern und Methoden sofort aus:

//+------------------------------------------------------------------+
//|                                             EventsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\EventBalanceOperation.mqh"
#include "..\Objects\Events\EventOrderPlaced.mqh"
#include "..\Objects\Events\EventOrderRemoved.mqh"
#include "..\Objects\Events\EventPositionOpen.mqh"
#include "..\Objects\Events\EventPositionClose.mqh"
//+------------------------------------------------------------------+
//| Kollektion der Kontoereignisse                                   |
//+------------------------------------------------------------------+
class CEventsCollection : public CListObj
  {
private:
   CListObj          m_list_events;                   // Liste der Ereignisse
   bool              m_is_hedge;                      // Flag des Hedging-Kontos
   long              m_chart_id;                      // Chart-ID des Steuerprogramms
   ENUM_TRADE_EVENT  m_trade_event;                   // Handelsereignis auf dem Konto
   CEvent            m_event_instance;                // Ereignisobjekt für die Suche nach einer Eigenschaft
   
//---- Erstellen eines Handelsereignisses in Abhängigkeit vom Auftragsstatus.
   void              CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market);
//--- Auswahl und Rückgabe der Liste der Pending-Marktorder
   CArrayObj*        GetListMarketPendings(CArrayObj* list);
//--- Auswahl und Rückgabe der Liste der histoirisch (1) entfernten Pending-Orders, (2) Deals, (3) alle Schließaufträge 
   CArrayObj*        GetListHistoryPendings(CArrayObj* list);
   CArrayObj*        GetListDeals(CArrayObj* list);
   CArrayObj*        GetListCloseByOrders(CArrayObj* list);
//--- Auswahl und Rückgabe der Liste von (1) allen Positionsaufträgen mach deren ID, (2) alle Deal-Positionen nach deren ID
//--- (3) alle Deals der Markteintritte nach der Positions-ID, (4) alle Deals der Marktaustritte nach ID
   CArrayObj*        GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id);
//--- Rückgabe des Gesamtvolumens aller Deals (1) IN, (2) OUT der Position nach deren ID
   double            SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id);
   double            SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id);
//--- Rückgabe des (1) ersten, (2) letzten und (3) schließenden Auftrags aus der Liste aller Positionsaufträge, (4) ein Auftrag nach Ticket
   COrder*           GetFirstOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetLastOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetCloseByOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetOrderByTicket(CArrayObj* list,const ulong order_ticket);
//--- Rückgabe des Flags des Ereignisobjekts in der Ereignisliste
   bool              IsPresentEventInList(CEvent* compared_event);
   
public:
//--- Auswählen der Ereignisse aus der Kollektion mit einer Zeit im Bereich von begin_time bis end_time.

   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Rückgabe des der gesamten Kollektionsliste des Ereignisses "wie besehen"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
//--- Aktualisieren der Ereignisliste
   void              Refresh(CArrayObj* list_history,
                             CArrayObj* list_market,
                             const bool is_history_event,
                             const bool is_market_event,
                             const int  new_history_orders,
                             const int  new_market_pendings,
                             const int  new_market_positions,
                             const int  new_deals);
//--- Setzen der Chart-ID des Steuerprogramms
   void              SetChartID(const long id)        { this.m_chart_id=id;         }
//--- Rückgabe des letzten Handelsereignisses auf dem Konto
   ENUM_TRADE_EVENT  GetLastTradeEvent(void)    const { return this.m_trade_event;  }
//--- Rücksetzen des letzten Handelsereignisses
   void              ResetLastTradeEvent(void)        { this.m_trade_event=TRADE_EVENT_NO_EVENT;   }
//--- Konstructor
                     CEventsCollection(void);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

Rücksetzen des Handelsereignisses in der Initialisierungsliste des Klassenkonstruktors,
Löschen der Kollektionsliste im Konstruktorkörper,
Sortieren nach der Ereigniszeit
,
Zuweisen der ID der Ereignis-Kollektionsliste,
Zuweisen des Flags für Hedge-Konten und
Zuweisen der Chart-ID des aktuellen Charts des Steuerprogramms
.

Betrachten wir die Methoden, die notwendig sind, damit die Klasse funktioniert.

Die folgenden Klassenmitglieder werden im Abschnitt 'private' der Klasse deklariert:

   CListObj          m_list_events;                   // Ereignisliste
   bool              m_is_hedge;                      // Flag des Hedging-Kontos
   long              m_chart_id;                      // Chart-ID des Steuerprogramms
   ENUM_TRADE_EVENT  m_trade_event;                   // Handelsereignis des Kontos
   CEvent            m_event_instance;                // Ereignisobjekt für die Suche nach einer Eigenschaft

Die Ereignisliste m_list_eventsbasiert auf CListObj. Es speichert Ereignisse, die auf dem Konto seit dem Start des Programms auftreten. Außerdem werden wir es verwenden, um die notwendige Anzahl von mehreren Ereignissen zu erhalten, die auf einmal aufgetreten sind.
Das Hedge Account Flagm_is_hedge dient zum Speichern und Empfangen der Kontoart. Der Flag-Wert (Kontoart) definiert den Block, der die auf dem Chart auftretenden Ereignisse behandelt
Die Chart-ID m_chart_idempfängt nutzerdefinierte Ereignisse, die auf dem Konto auftreten. Die ID wird an Ereignisobjekte und zurück an das Chart gesendet. Die ID kann vom Steuerungsprogramm aus mit der dafür geschaffenen Methode eingestellt werden.
Das Handelsereignis m_trade_eventspeichert das letzte Ereignis auf dem Konto.
Das Ereignisobjekt m_event_instance dient zur Suche nach einer Eigenschaft — ein spezielles Beispielobjekt zur internen Verwendung in der Methode, das die Liste der Ereignisse mit angegebenen Daten des Suchbereichsbeginns und -endes zurückgibt. Wir haben bereits eine ähnliche Methode im dritten Teil der Bibliotheksbeschreibung analysiert, um zu diskutieren, wie man die Suche in den Listen nach verschiedenen Kriterien anordnet.

Hier im 'private' Bereich sehen Sie die für die Klassenoperation notwendigen Methoden:

//---- Erstellen eines Handelsereignisses in Abhängigkeit vom Auftragsstatus.
   void              CreateNewEvent(COrder* order,CArrayObj* list_history);
//--- Auswahl und Rückgabe der Liste der Pending-Marktorder
   CArrayObj*        GetListMarketPendings(CArrayObj* list);
//--- Auswahl und Rückgabe der Liste der historischen (1) Aufträge, (2) entfernter Pending-Orders 
//--- (3) Deals, (4) alle Positionsaufträge nach deren ID, (5) all Positionsdeals nach deren ID
//--- (6) alle Deals des Markteintritts nach der Positions-ID, (7) alle Deals des Marktverlassens nach deren Positions-ID
//--- (7) alle Aufträge zum Schließen
   CArrayObj*        GetListHistoryOrders(CArrayObj* list);
   CArrayObj*        GetListHistoryPendings(CArrayObj* list);
   CArrayObj*        GetListDeals(CArrayObj* list);
   CArrayObj*        GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllCloseByOrders(CArrayObj* list);
//--- Rückgabe des Gesamtvolumens aller Deals (1) IN, (2) OUT der Position nach deren ID
   double            SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id);
   double            SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id);
//--- Rückgabe des (1) ersten, (2) letzten und (3) schließenden Auftrags aus der Liste aller Positionsaufträge, (4) ein Auftrag nach Ticket
   COrder*           GetFirstOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetLastOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetCloseByOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetOrderByTicket(CArrayObj* list,const ulong order_ticket);
//--- Rückgabe des Flags des Ereignisobjekts in der Ereignisliste
   bool              IsPresentEventInList(CEvent* compared_event);

Die Methode CreateNewEvent(), die ein Handelsereignis in Abhängigkeit vom Auftragsstatus erzeugt, wird in der Methode Refresh() der Hauptklasse verwendet. Wir werden darauf zurückkommen, wenn wir die Methode Refresh() diskutieren.

Die Methoden zum Empfangen der Listen der verschiedenen Auftragstypen sind recht einfach — die Auswahl durch eine bestimmte Eigenschaft wurde im dritten Teil der Bibliotheksbeschreibung diskutiert. An dieser Stelle werden wir nur kurz erwähnen, dass einige Methoden aus mehreren Iterationen der Selektion nach notwendigen Eigenschaften bestehen.

Die Methode zum Empfangen der Liste der Pending-Orders:

//+------------------------------------------------------------------+
//| Nur Pending-Orders aus der Liste auswählen                       |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListMarketPendings(CArrayObj* list)
  {
   if(list.Type()!=COLLECTION_MARKET_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. List is not a list of market collection"));
      return NULL;
     }
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL);
   return list_orders;
  }
//+------------------------------------------------------------------+

Der Typ der an die Methode übergebenen Liste wird zuerst überprüft. Wenn es sich nicht um eine Liste der Marktkollektion handelt, wird die Fehlermeldung angezeigt und eine leere Liste zurückgegeben.

Anschließend werden Aufträge mit dem Status "Market pending order" aus der an die Methode übergebenen Liste ausgewählt und die erhaltene Liste zurückgegeben.

Die Methoden zum Empfangen von Listen mit entfernten Pending-Orders, Deals und Schließen von Aufträgen, die beim Schließen einer Position durch einer entgegengesetzten Position platziert wurden:

//+------------------------------------------------------------------+
//| Auswählen nur der gelöschten Pending-Orders aus der Liste        |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListHistoryPendings(CArrayObj* list)
  {
   if(list.Type()!=COLLECTION_HISTORY_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"));
      return NULL;
     }
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
   return list_orders;
  }
//+------------------------------------------------------------------+
//| Auswahl nur eines Deals aus der Liste                            |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListDeals(CArrayObj* list)
  {
   if(list.Type()!=COLLECTION_HISTORY_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"));
      return NULL;
     }
   CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL);
   return list_deals;
  }
//+------------------------------------------------------------------+
//|  Rückgabe der Liste aller Aufträge CloseBy aus der Liste         |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListCloseByOrders(CArrayObj *list)
  {
   if(list.Type()!=COLLECTION_HISTORY_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"));
      return NULL;
     }
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL);
   return list_orders;
  }
//+------------------------------------------------------------------+

Genau wie bei der Rückgabe der Liste der aktiven Pending-Orders:

Der Listentyp wird überprüft und wenn es sich nicht um die historische Kollektion handelt, wird die Nachricht angezeigt und NULL zurückgegeben.
Dann werden Aufträge mit den Zuständen "Removed pending order" und "Deal" aus der an die Methode übergebenen Liste ausgewählt oder Aufträge vom Typ ORDER_TYPE_CLOSE_BY je nach Methode ausgewählt und die erhaltene Liste zurückgegeben.

Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Aufträge nach ihrer ID:

//+------------------------------------------------------------------+
//| Liefert die Liste aller Positionsorders nach ihrer ID            |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id)
  {
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL);
   list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL);
   return list_orders;
  }
//+------------------------------------------------------------------+

Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate Liste aller Objekte, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Als Nächstes werden alle Deals aus der erhaltenen Liste entfernt, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben hat.

Methode zum Erhalten einer Liste aller Deals, die zu einer Position gehören, nach ihrer ID:

//+------------------------------------------------------------------+
//| Rückgabe der Liste aller Positionsdeals nach ihrer ID            |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListAllDealsByPosID(CArrayObj *list,const ulong position_id)
  {
   if(list.Type()!=COLLECTION_HISTORY_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"));
      return NULL;
     }
   CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL);
   list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL);
   return list_deals;
  }
//+------------------------------------------------------------------+

Der Listentyp wird zuerst überprüft und wenn es sich nicht um die historische Kollektion handelt, wird die Nachricht angezeigt und NULL zurückgegeben.
Als Nächstes bilden Sie mit Hilfe der an die Methode übergebenen Liste eine separate Liste aller Objekte, die einen Zeiger auf die Positions-ID enthalten, die über ihren Parameter an die Methode übergeben werden.
Danach befinden sich nur noch Deals in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben hat.

Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Markteintrittdeals nach ihrer ID:

//+------------------------------------------------------------------+
//| Rückgabe der Liste aller Markteintrittsdeals (IN)                |
//| nach der Positions-ID                                            |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListAllDealsInByPosID(CArrayObj *list,const ulong position_id)
  {
   CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id);
   list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_IN,EQUAL);
   return list_deals;
  }
//+------------------------------------------------------------------+

Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate Liste aller Deals, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Danach befinden sich nur noch Deals des Typs DEAL_ENTRY_IN in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben hat.

Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Marktaustrittdeals nach ihrer ID:

//+------------------------------------------------------------------+
//| Rückgabe der Liste aller Marktaustrittsdeals (OUT)               |
//| nach der Positions-ID                                            |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListAllDealsOutByPosID(CArrayObj *list,const ulong position_id)
  {
   CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id);
   list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_OUT,EQUAL);
   return list_deals;
  }
//+------------------------------------------------------------------+

Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate Liste aller Deals, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Danach befinden sich nur noch Deals des Typs DEAL_ENTRY_OUT in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was genau die Methode im aufrufenden Programm zurückgegeben hat.

Die Methode gibt das Gesamtvolumen alle Deals der Markteintrittspositionen nach deren ID zurück:

//+------------------------------------------------------------------+
//| Rückgabe des Gesamtvolumens aller Deals von IN-Positinen         |
//| nach der ID                                                      |
//+------------------------------------------------------------------+
double CEventsCollection::SummaryVolumeDealsInByPosID(CArrayObj *list,const ulong position_id)
  {
   double vol=0.0;
   CArrayObj* list_in=this.GetListAllDealsInByPosID(list,position_id);
   if(list_in==NULL)
      return 0;
   for(int i=0;i<list_in.Total();i++)
     {
      COrder* deal=list_in.At(i);
      if(deal==NULL)
         continue;
      vol+=deal.Volume();
     }
   return vol;
  }
//+------------------------------------------------------------------+

Zuerst erhalten wir die Liste aller Markteintrittspositionsdeals, dann summen wir die Volumina aller Deals in einer Schleife. Das resultierende Volumen wird an das aufrufende Programm zurückgegeben. Wenn die an die Methode übergebene Liste leer ist oder es sich nicht um eine historische Kollektionsliste handelt, gibt die Methode Null zurück.

Die Methode gibt das Gesamtvolumen aller Transaktionen einer Marktaustrittsposition nach ihrer ID zurück:

//+--------------------------------------------------------------------------------------+
//| Rückgabe des Gesamtvolumens aller Deals der OUT-Position nach deren                  |
//| ID (Anteil beim Schließen durch eine entgegengesetzte Position wird berücksichtigt)  |
//+--------------------------------------------------------------------------------------+
double CEventsCollection::SummaryVolumeDealsOutByPosID(CArrayObj *list,const ulong position_id)
  {
   double vol=0.0;
   CArrayObj* list_out=this.GetListAllDealsOutByPosID(list,position_id);
   if(list_out!=NULL)
     {
      for(int i=0;i<list_out.Total();i++)
        {
         COrder* deal=list_out.At(i);
         if(deal==NULL)
            continue;
         vol+=deal.Volume();
        }
     }
   CArrayObj* list_by=this.GetListCloseByOrders(list);
   if(list_by!=NULL)
     {
      for(int i=0;i<list_by.Total();i++)
        {
         COrder* order=list_by.At(i);
         if(order==NULL)
            continue;
         if(order.PositionID()==position_id || order.PositionByID()==position_id)
           {
            vol+=order.Volume();
           }
        }
     }
   return vol;
  }
//+------------------------------------------------------------------+

Wenn ein Teil einer Position, dessen ID an die Methode übergeben wurde, am Schließen einer anderen Position teilgenommen hat (als eine entgegengesetzte), oder ein Teil der Position durch eine entgegengesetzte geschlossen wurde, wird dies in der Position nicht berücksichtigt. Stattdessen wird sie im Eigenschaftsfeld ORDER_PROP_POSITION_BY_ID des letzten Abschlussorder der Position berücksichtigt. Daher hat diese Methode zwei Suchvorgänge nach geschlossenen Volumina — nach Deals und nach Aufträgen zu schließen.

Zuerst erhalten wir die Liste aller Marktaustrittspositionsdeals, dann summen wir die Volumina aller Deals in einer Schleife.
Als Nächstes erhalten wir die Liste aller in der historischen Schließaufträge der historischen Liste und verwenden eine Schleife, um die Zugehörigkeit des ausgewählten Auftrags zu der Position zu überprüfen, deren ID der Methode übergeben wurde. Wenn der ausgewählte Auftrag beim Schließen einer Position beteiligt war, wird ihr Volumen zum Gesamtvolumen addiert.
Das resultierende Volumen wird an das aufrufende Programm zurückgegeben. Wenn die an die Methode übergebene Liste leer ist oder es sich nicht um eine historische Kollektionsliste handelt, gibt die Methode Null zurück.

Die Methode gibt den ersten (Öffnungs-)Positionsauftrag nach ihrer ID zurück:

//+------------------------------------------------------------------+
//| Rückgabe des ersten Auftrags der Liste aller Positionsaufträge   |
//+------------------------------------------------------------------+
COrder* CEventsCollection::GetFirstOrderFromList(CArrayObj* list,const ulong position_id)
  {
   CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id);
   if(list_orders==NULL || list_orders.Total()==0) return NULL;
   list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list_orders.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Zuerst erhalten wir die Liste aller Positionsaufträge. Die erhaltene Liste ist sortiert nach Eröffnungszeit und wir nehmen deren erstes Element. Das wird als erster Positionsauftrag verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.

Die Methode gibt den letzte Positionsauftrag nach dessen ID zurück:

//+------------------------------------------------------------------+
//| Rückgabe des letzten Auftrags der Liste aller Positionsaufträge  |
//+------------------------------------------------------------------+
COrder* CEventsCollection::GetLastOrderFromList(CArrayObj* list,const ulong position_id)
  {
   CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id);
   if(list_orders==NULL || list_orders.Total()==0) return NULL;
   list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list_orders.At(list_orders.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Zuerst erhalten wir die Liste aller Positionsaufträge. Die erhaltene Liste ist sortiert nach Eröffnungszeit und wir nehmen deren letztes Element. Das wird als letzter Positionsauftrag verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.

Die Methode gibt den letzten Positionsauftrag nach dessen ID zurück: (ORDER_TYPE_CLOSE_BY Auftragstyp):

//+------------------------------------------------------------------+
//| Rückgabe des letzten Schließauftrags                             |
//| aus der Liste aller Positionsaufträge                            |
//+------------------------------------------------------------------+
COrder* CEventsCollection::GetCloseByOrderFromList(CArrayObj *list,const ulong position_id)
  {
   CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id);
   list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL);
   if(list_orders==NULL || list_orders.Total()==0) return NULL;
   list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list_orders.At(list_orders.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Da beim teilweisen Schließen durch eine entgegengesetzte Position möglich sind und die Volumina der beiden entgegengesetzten Positionen ungleich sein können, ist der Schließauftrag möglicherweise nicht der einzige der Positionsaufträge. Daher sucht die Methode nach solchen Aufträgen und gibt den letzte zurück — es ist der letzte Auftrag, der ein Ereignis auslöst.

Zuerst erhalten wir die Liste aller Positionsaufträge. Dann erhalten wir aus der erhaltenen Liste die Liste, die nur Schließaufträge (vom Typ ORDER_TYPE_CLOSE_BY) enthält. Die so erhaltene Liste ist sortiert nach der Eröffnungszeit, und es wird deren letztes Element genommen. Es wird als letzter Schließauftrag der Position verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.

Wenn wir durch eine entgegengesetzte Position schließen, kann es Situationen geben, in denen die Bibliothek zwei identische Ereignisse sieht: zwei Positionen sind geschlossen, und nur eine von ihnen hat ist ein Schließauftrag plus zwei Deals. Um also nicht das gleiche Ereignis in der Kollektion zu duplizieren, sollten wir zunächst das Vorhandensein genau des gleichen Ereignisses in der Kollektionsliste der Ereignisse überprüfen und, wenn es nicht vorhanden ist, das Ereignis in die Liste aufnehmen.

Die Methode, die einen Auftrag nach dem Ticket zurückgibt:

//+------------------------------------------------------------------+
//| Rückgabe des Auftrags nach dem Ticket                            |
//+------------------------------------------------------------------+
COrder* CEventsCollection::GetOrderByTicket(CArrayObj *list,const ulong order_ticket)
  {
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL);
   list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TICKET,order_ticket,EQUAL);
   if(list_orders==NULL || list_orders.Total()==0) return NULL;
   COrder* order=list_orders.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Zuerst erstellen wir nur die Liste der Aufträge, dann sortieren wir die Liste nach dem Ticket, das der Methode übergeben wurde. Infolgedessen geben wir entweder NULL (wenn es kein Auftrag mit einem solchen Ticket gibt) oder die Ticketnummer zurück.

Die Methode, die das in der Liste vorhandene Ereignis zurückgibt, wird verwendet, um zu überprüfen, ob das Ereignis in der Liste enthalten ist:

//+------------------------------------------------------------------+
//| Rückgabe des Flags des Ereignisobjekts in der Ereignisliste      |
//+------------------------------------------------------------------+
bool CEventsCollection::IsPresentEventInList(CEvent *compared_event)
  {
   int total=this.m_list_events.Total();
   if(total==0)
      return false;
   for(int i=total-1;i>=0;i--)
     {
      CEvent* event=this.m_list_events.At(i);
      if(event==NULL)
         continue;
      if(event.IsEqual(compared_event))
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Der Zeiger auf das zu vergleichende Ereignisobjekt wird der Methode übergeben. Wenn die Kollektionsliste leer ist, wird sofort 'false' zurückgegeben, was bedeutet, dass es kein solches Ereignis in der Liste gibt. Danach wird das nächste Ereignis aus der Liste in einer Schleife genommen und mit dem Ereignis verglichen, das der Methode unter Verwendung der Methode IsEqual() des abstrakten Ereignisses CEvent übergeben wurde. Wenn die Methode 'true' zurückgibt, ist ein solches Ereignisobjekt in der Kollektionsliste der Ereignisse vorhanden. Das Vervollständigen der Schleife oder das Erreichen der letzten Methodenzeichenkette bedeutet, dass es kein Ereignis in der Liste gibt, und es wird 'false' zurückgegeben.

Deklarieren der Methoden im 'public' Teil der Klasse:

public:
//--- Auswählen der Ereignisse aus der Kollektion mit einer Zeit im Bereich von begin_time bis end_time.

   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Rückgabe des der gesamten Kollektionsliste des Ereignisses "wie besehen"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
//--- Aktualisieren der Ereignisliste
   void              Refresh(CArrayObj* list_history,
                             CArrayObj* list_market,
                             const bool is_history_event,
                             const bool is_market_event,
                             const int  new_history_orders,
                             const int  new_market_pendings,
                             const int  new_market_positions,
                             const int  new_deals);
//--- Setzen der Chart-ID des Steuerprogramms
   void              SetChartID(const long id)        { this.m_chart_id=id;         }
//--- Rückgabe des letzten Handelsereignisses auf dem Konto
   ENUM_TRADE_EVENT  GetLastTradeEvent(void)    const { return this.m_trade_event;  }
//--- Rücksetzen des letzten Handelsereignisses
   void              ResetLastTradeEvent(void)        { this.m_trade_event=TRADE_EVENT_NO_EVENT;   }
//--- Konstructor
                     CEventsCollection(void);

Ich habe die Methoden zum Empfangen der vollständigen Liste, die Listen nach einem Datumsbereich und nach ausgewählten Ganzzahl-, Double- und String-Eigenschaften im dritten Teil der Bibliotheksbeschreibung beschrieben. Hier zeige ich Ihnen nur die Auflistung dieser Methoden, damit Sie sie selbst analysieren können.

Die Methode zur Übernahme der Liste der Ereignisse im angegebenen Datumsbereich:

//+------------------------------------------------------------------+
//| Auswählen der Ereignisse aus der Kollektion nach der Zeit        |
//| im Bereich von begin_time bis end_time                           |
//+------------------------------------------------------------------+
CArrayObj *CEventsCollection::GetListByTime(const datetime begin_time=0,const datetime end_time=0)
  {
   CArrayObj *list=new CArrayObj();
   if(list==NULL)
     {
      ::Print(DFUN+TextByLanguage("Ошибка создания временного списка","Error creating temporary list"));
      return NULL;
     }
   datetime begin=begin_time,end=(end_time==0 ? END_TIME : end_time);
   if(begin_time>end_time) begin=0;
   list.FreeMode(false);
   ListStorage.Add(list);
   //---
   this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,begin);
   int index_begin=this.m_list_events.SearchGreatOrEqual(&m_event_instance);
   if(index_begin==WRONG_VALUE)
      return list;
   this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,end);
   int index_end=this.m_list_events.SearchLessOrEqual(&m_event_instance);
   if(index_end==WRONG_VALUE)
      return list;
   for(int i=index_begin; i<=index_end; i++)
      list.Add(this.m_list_events.At(i));
   return list;
  }
//+------------------------------------------------------------------+

Die Hauptmethode, die aus dem Basis-Bibliotheksobjekt aufgerufen wird, wenn eines der Ereignisse eintritt, ist Refresh().
Derzeit arbeitet die Methode auf Hedge-Konten für MQL5.

Die Methode erhält die Zeiger auf die Listen der Kollektionen von Markt- und historischen Aufträgen, Deals und Positionen sowie die Daten über die Anzahl der neu erschienenen oder entfernten Aufträge, offenen und geschlossenen Positionen und neuen Deals.
Abhängig von einer geänderten Liste wird die erforderliche Anzahl von Aufträgen oder Deals entsprechend der Anzahl der Aufträge/Positionen/Deals in einer Schleife ermittelt, und für jeden von ihnen wird die Methode CreateNewEvent() zum Erstellen von Ereignissen und zum Eintragen in der Kollektionsliste aufgerufen.
Somit wird die neue Methode zur Ereigniserzeugung für jedes eingetretene Ereignis aufgerufen, während das Ereignis in die Kollektionsliste aufgenommen wird und das aufrufende Programm über alle Ereignisse informiert wird, indem eine nutzerdefinierte Nachricht an das Diagramm des aufrufenden Programms gesendet wird.

Die Variable m_trade_event der Klasse erhält den Wert des zuletzt aufgetretenen Ereignisses. Die 'public' Methode GetLastTradeEvent() gibt den Wert des letzten Handelsgeschehens zurück. Es gibt auch die Methode zum Zurücksetzen des letzten Handelsereignisses (ähnlich wie bei GetLastError() und ResetLastError()).
Darüber hinaus gibt es Methoden, die die Kollektionslisten der Ereignisse sowohl vollständig, nach einem Zeitbereich und bestimmten Kriterien zurückgeben. Das aufrufende Programm weiß immer, dass ein Ereignis oder mehrere Ereignisse aufgetreten sind, und es ist möglich, die Liste aller dieser Ereignisse in einer erforderlichen Menge anzufordern und sie gemäß der Logik des eingebauten Programms zu behandeln.

Betrachten wir den Code der Methoden Refresh() und CreateNewEvent().

Die Methode zum Aktualisieren der Kollektionsliste der Ereignisse:

//+------------------------------------------------------------------+
//| Aktualisieren der Ereignisliste                                  |
//+------------------------------------------------------------------+
void CEventsCollection::Refresh(CArrayObj* list_history,
                                CArrayObj* list_market,
                                const bool is_history_event,
                                const bool is_market_event,
                                const int  new_history_orders,
                                const int  new_market_pendings,
                                const int  new_market_positions,
                                const int  new_deals)
  {
//--- Rückkehren, wenn die Liste leer ist
   if(list_history==NULL || list_market==NULL)
      return;
//--- Im Falle eines Hedging-Kontos
   if(this.m_is_hedge)
     {
      //--- Wenn das Ereignis in der Umgebung des Marktes existiert
      if(is_market_event)
        {
         //--- wenn sich die Anzahl der platzierten Pending-Orders erhöht hat
         if(new_market_pendings>0)
           {
            //--- Empfangen der Liste der neuesten, platzierten Pending-Orders
            CArrayObj* list=this.GetListMarketPendings(list_market);
            if(list!=NULL)
              {
               //--- Sortieren der neuen Liste nach der Platzierungszeit der Orders
               list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); 
               //--- Nehmen der Order-Anzahl, die gleich der Anzahl neu platzierten vom Ende der Liste ist, in einer Schleife (die letzten N Ereignisse)
               int total=list.Total(), n=new_market_pendings;
               for(int i=total-1; i>=0 && n>0; i--,n--)
                 {
                  //--- Erhalt der Order von der Liste, wenn es eine Pending-Order ist, wird das Handelsereignis gesetzt
                  COrder* order=list.At(i);
                  if(order!=NULL && order.Status()==ORDER_STATUS_MARKET_PENDING)
                     this.CreateNewEvent(order,list_history,list_market);
                 }
              }
           }
        }
      //--- Wenn das Ereignis in Kontohistorie existiert
      if(is_history_event)
        {
         //--- Wenn sich die Anzahl historischer Aufträge erhöht hat
         if(new_history_orders>0)
           {
            //--- Erhalt der Liste nur der entfernten Pending-Orders
            CArrayObj* list=this.GetListHistoryPendings(list_history);
            if(list!=NULL)
              {
               //--- Sortieren der Liste nach der Entfernungszeit der Orders
               list.Sort(SORT_BY_ORDER_TIME_CLOSE_MSC);
               //--- Nehmen der Order-Anzahl gleich der Anzahl neu entfernten vom Ende der Liste in einer Schleife (die letzten N Ereignisse)
               int total=list.Total(), n=new_history_orders;
               for(int i=total-1; i>=0 && n>0; i--,n--)
                 {
                  //--- Empfangen einer Order von der Liste. Wenn das eine entfernte Pending-Order ist, wird das Handelsereignis gesetzt
                  COrder* order=list.At(i);
                  if(order!=NULL && order.Status()==ORDER_STATUS_HISTORY_PENDING)
                     this.CreateNewEvent(order,list_history,list_market);
                 }
              }
           }
         /--- Wenn sich die Anzahl der Deals sich erhöht hat
         if(new_deals>0)
           {
            //--- Empfangen der Liste nur der Deals
            CArrayObj* list=this.GetListDeals(list_history);
            if(list!=NULL)
              {
               //--- Sortieren der neuen Liste nach der Dealzeit
               list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
               //--- Nehmen der Anzahl der Deals, die gleich der Anzahl der neuen vom Ende der Liste ist, in einer Schleife (die letzten N Ereignisse)
               int total=list.Total(), n=new_deals;
               for(int i=total-1; i>=0 && n>0; i--,n--)
                 {
                  //--- Erhalt eines Deals von der Liste und setzen des Handelsereignisses
                  COrder* order=list.At(i);
                  if(order!=NULL)
                     this.CreateNewEvent(order,list_history,list_market);
                 }
              }
           }
        }
     }
   //--- Im Falle eines Netting-Kontos
   else
     {
      
     }
  }  
//+------------------------------------------------------------------+

Der einfache Code der Methode enthält alle notwendigen Bedingungen und Aktionen, wenn diese Bedingungen erfüllt sind. Ich glaube, hier ist alles ziemlich klar. Derzeit werden Ereignisse eines Hedging-Kontos behandelt.

Betrachten wir die Methode zum Erstellen eines neuen Ereignisses:

//+------------------------------------------------------------------+
//| Erstellen eines Handelsereignisses abhängig vom Auftragsstatus   |
//+------------------------------------------------------------------+
void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market)
  {
   int trade_event_code=TRADE_EVENT_FLAG_NO_EVENT;
   ENUM_ORDER_STATUS status=order.Status();
//--- Pending-Order platziert
   if(status==ORDER_STATUS_MARKET_PENDING)
     {
      trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED;
      CEvent* event=new CEventOrderPlased(trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                       // Zeit des Ereignisses
         event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE);                       // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());                    // Dealtyp des Ereignisses
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());                     // Auftragsticket des Ereignisses
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());                   // Auftragstyp des Ereignisses
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());                // Auftragstyp des Ereignisses
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());                    // Auftragsticket des Ereignisses
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());                 // Auftragsticket
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                       // Positions-ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());                  // ID der entgegengesetzten Position
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                            // Magicnummer des Auftrags
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());              // Zeit des Auftrags
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                        // Preis, bei dem ein Ereignis auftritt
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                         // Preis zu dem ein Auftrag platziert wurde
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());                       // Schließpreis des Auftrags
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                            // StopLoss des Auaftrags
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                          // TakeProfit des Auftrags
         event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume());                        // Verlangtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Ausgeführtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent());                 // Verbliebenes (nicht ausgeführtes) Volumen
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                                // Gewinn
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                                // Auftragssymbol
         //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- Pending-Order entfernt
   if(status==ORDER_STATUS_HISTORY_PENDING)
     {
      trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED;
      CEvent* event=new CEventOrderRemoved(trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         ENUM_EVENT_REASON reason=
           (
            order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL :
            order.State()==ORDER_STATE_EXPIRED  ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE
           );
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC());             // Ereigniszeit
         event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                         // Ereignisgrund (aus ENUM_EVENT_REASON)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());           // Ereignis der Auftragstyp
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());            // Ereignis Auftragsticket
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());          // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());       // Auftragstyp, der ein Deal-Ereignis Type auslöst (der erste Auftrag zur Position)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());           // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());        // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());              // Positions-ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());         // ID der entgegengesetzten Positions
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                   // Magicnummer des Auftrags
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());     // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());               // Ereignispreis
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                // Eröffnungspreis des Auftrags
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());              // Schließpreis des Auftrags
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                   // StopLoss Auftragspreis
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                 // TakeProfit Auftragspreis
         event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume());               // Verlangtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Ausgeführtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent());        // Verbliebenes (nicht ausgeführtes) Volumen
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                       // Gewinn
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                       // Symbol des Auftrags
         //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- Hinzufügen des Ereignis-Objekts, falls es nicht in der Liste ist
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- Position eröffnet (__MQL4__)
   if(status==ORDER_STATUS_MARKET_POSITION)
     {
      trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED;
      CEvent* event=new CEventPositionOpen(trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen());              // Ereigniszeit
         event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE);           // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());        // Deal-Typ des Ereignisses
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());         // Deal-Ticket des Ereignisses
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());       // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());    // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());        // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());     // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());           // Positions-ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());      // ID der entgegengesetzten Positions
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                // Magicnummer von Order/Deal/Position
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen());     // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());            // Ereignispreis
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());             // Eröffnungspreis von Order/Deal/Position
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());           // Schließpreis von Order/Deal/Position
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                // StopLoss der Position
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());              // TakeProfit der Position
         event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume());            // Verlangtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume());           // Ausgeführtes Volumen
         event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent());     // Verbliebenes (nicht ausgeführtes) Volumen
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                    // Gewinn
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                    // Symbol des Auftrags
         //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- Hinzufügen des Ereignis-Objekts, falls es nicht in der Liste ist
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- Neuer Deal (__MQL5__)
   if(status==ORDER_STATUS_DEAL)
     {
      //--- Neue Saldenoperationen
      if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL)
        {
         trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE;
         CEvent* event=new CEventBalanceOperation(trade_event_code,order.Ticket());
         if(event!=NULL)
           {
            ENUM_EVENT_REASON reason=
              (
               (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) :
               (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT)
              );
            event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());           // Ereigniszeit
            event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                      // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
            event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());        // Deal-Typ des Ereignisses
            event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());         // Auftragsticket des Ereignisses
            event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());       // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
            event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());    // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());        // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());     // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
            event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());           // Positions-ID
            event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());      // ID der entgegengesetzten Positions
            event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                // Magicnummer von Order/Deal/Position
            event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());  // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
            event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());            // Ereignispreis
            event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());             // Eröffnungspreis von Order/Deal/Position
            event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());           // Schließpreis von Order/Deal/Position
            event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                // Dealpreis des StopLoss'
            event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());              // Dealpreis des TakeProfits
            event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume());            // Verlangtes Volumen
            event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume());           // Ausgeführtes Volumen
            event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent());     // Verbliebenes (nicht ausgeführtes) Volumen
            event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                    // Gewinn
            event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                    // Symbol des Auftrags
            //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
            event.SetChartID(this.m_chart_id);
            event.SetTypeEvent();
            //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist
            if(!this.IsPresentEventInList(event))
              {
               //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses
               this.m_list_events.InsertSort(event);
               event.SendEvent();
               this.m_trade_event=event.TradeEvent();
              }
            //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht
            else
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
               delete event;
              }
           }
        }
      //--- Wenn es keine Saldenoperation ist
      else
        {
         //--- Markteintritt
         if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN)
           {
            trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED;
            int reason=EVENT_REASON_DONE;
            //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und berechnen des Gesamtvolumens
            double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID());
            //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge
            ulong order_ticket=order.GetProperty(ORDER_PROP_DEAL_ORDER);
            COrder* order_first=this.GetOrderByTicket(list_history,order_ticket);
            COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID());
            //--- Wenn es keinen letzten Auftrag gibt, müssen erster und letzter Positionsauftrag übereinstimmen
            if(order_last==NULL)
               order_last=order_first;
            if(order_first!=NULL)
              {
               //--- Wenn das Auftragsvolumen teilweise eröffnet wurde, ist das eine teilweise Ausführung
               if(this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume())
                 {
                  trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
                  reason=EVENT_REASON_DONE_PARTIALLY;
                 }
               //--- Wenn der Eröffnungsauftrag  eine Pending-Order ist, wurde die Pending-Order ausgelöst
               if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY)
                 {
                  trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED;
                  //--- Wenn ein Auftrag teilweise ausgeführt wurde, wird die teilweise Auftragsausführung als Grund des Ereignisses gesetzt
                  reason=
                    (this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume() ? 
                     EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : 
                     EVENT_REASON_ACTIVATED_PENDING
                    );
                 }
               CEvent* event=new CEventPositionOpen(trade_event_code,order.PositionID());
               if(event!=NULL)
                 {
                  event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                 // Ereigniszeit (Eröffnungszeit der Position)
                  event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
                  event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());              // Ereignis-Typ des Deals
                  event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // Deal-Ticket des Ereignisses
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());    // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());     // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder());        // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket());         // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // Positions-ID
                  event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID());       // ID der entgegengesetzten Positions
                  event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // Magicnummer von Order/Deal/Position 
                  event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());  // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                  // Ereignispreis (Eröffnungspreis der Position)
                  event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());             // Eröffnungspreis des Auftrags (erster Auftrag des Eröffnungspreises der Position)
                  event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose());            // Schließpreis des Auftrags (letzter Auftrag des Schließpreises der Position)
                  event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                // StopLoss (StopLoss des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());              // TakeProfit (TakeProfit des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order_first.Volume());            // Verlangtes Volumen
                  event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,volume_in);                      // Ausgeführtes Volumen
                  event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order_first.Volume()-volume_in);  // Verbliebenes (nicht ausgeführtes) Volumen
                  event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull());                      // Gewinn
                  event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // Symbol des Auftrags
                  //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
                  event.SetChartID(this.m_chart_id);
                  event.SetTypeEvent();
                  //--- Hinzufügen eines Ereignisobjekts, wenn es nicht in der Liste ist
                  if(!this.IsPresentEventInList(event))
                    {
                     this.m_list_events.InsertSort(event);
                     //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses
                     event.SendEvent();
                     this.m_trade_event=event.TradeEvent();
                    }
                  //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht
                  else
                    {
                     ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
                     delete event;
                    }
                 }
              }
           }
         //--- Marktaustritt
         else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT)
           {
            trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED;
            int reason=EVENT_REASON_DONE;
            //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge
            COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID());
            COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID());
            if(order_first!=NULL && order_last!=NULL)
              {
               //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens
               double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID());
               double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID());
               //--- Berechnen des aktuellen Volumens der geschlossenen Position
               int dgl=(int)DigitsLots(order.Symbol());
               double volume_current=::NormalizeDouble(volume_in-volume_out,dgl);
               //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es eine teilweise Ausführung
               if(volume_current>0)
                 {
                  trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
                 }
               //--- Wenn ein Schließauftrag teilweise ausgeführt wurde, wird die teilweise Auftragsausführung als Grund des Ereignisses
               if(order_last.VolumeCurrent()>0)
                 {
                  reason=EVENT_REASON_DONE_PARTIALLY;
                 }
               //--- Wenn das Schließ-Flag auf StopLoss für das Schließen der Position gesetzt wurde, dann wurde StopLoss ausgeführt
               //--- Wenn ein StopLoss teilweise ausgeführt wurde, wird die teilweise StopLoss-Ausführung als Grund des Ereignisses gesetzt
               if(order_last.IsCloseByStopLoss())
                 {
                  trade_event_code+=TRADE_EVENT_FLAG_SL;
                  reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL);
                 }
               //--- Wenn das Schließ-Flag auf TakeProfit für das Schließen der Position gesetzt wurde, dann wurde TakeProfit ausgeführt
               //--- Wenn ein TakeProfit teilweise ausgeführt wurde, wird die teilweise TakeProfit-Ausführung als Grund des Ereignisses gesetzt
               else if(order_last.IsCloseByTakeProfit())
                 {
                  trade_event_code+=TRADE_EVENT_FLAG_TP;
                  reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP);
                 }
               //---
               CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID());
               if(event!=NULL)
                 {
                  event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                 // Ereigniszeit (Schließzeit der Positioon)
                  event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
                  event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());              // Ereignis-Typ des Deals
                  event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // Deal-Ticket des Ereignisses
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());    // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder());        // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());     // Auftragsticket auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket());         // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // Positions-ID
                  event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID());       // ID der entgegengesetzten Positions
                  event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // Magicnummer von Order/Deal/Position
                  event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());  // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                  // Ereignispreis (Schließpreis die Position)
                  event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());             // Eröffnungspreis des Auftrags (erster Auftrag des Eröffnungspreises der Position)
                  event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose());            // Schließpreis des Auftrags (letzter Auftrag des Schließpreises der Position)
                  event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                // StopLoss (StopLoss des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());              // TakeProfit (TakeProfit des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_VOLUME_INITIAL,volume_in);                       // Anfangsvolumen
                  event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume());                 // Schließvolumen
                  event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_in-volume_out);            // Verbliebenes (aktuelles) Volumen
                  event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull());                      // Gewinn
                  event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // Symbol des Auftrags
                  //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
                  event.SetChartID(this.m_chart_id);
                  event.SetTypeEvent();
                  //--- Hinzufügen eines Ereignisobjekts, wenn es nicht in der Liste ist
                  if(!this.IsPresentEventInList(event))
                    {
                     this.m_list_events.InsertSort(event);
                     //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses
                     event.SendEvent();
                     this.m_trade_event=event.TradeEvent();
                    }
                  //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht
                  else
                    {
                     ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
                     delete event;
                    }
                 }
              }
           }
         //--- entgegengesetzte Position
         else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY)
           {
            trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED;
            int reason=EVENT_REASON_DONE_BY_POS;
            //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge
            COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID());
            COrder* order_close=this.GetCloseByOrderFromList(list_history,order.PositionID());
            if(order_first!=NULL && order_close!=NULL)
              {
               //--- Hinzufügen des Flags beim Schließen durch eine Gegenposition
               trade_event_code+=TRADE_EVENT_FLAG_BY_POS;
               //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens
               double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID());
               double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID());//+order_close.Volume();
               //--- Berechnen des aktuellen Volumens der geschlossenen Position
               int dgl=(int)DigitsLots(order.Symbol());
               double volume_current=::NormalizeDouble(volume_in-volume_out,dgl);
               //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens
               double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID());
               double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID());//+order_close.Volume();
               //--- Berechnen des aktuellen Volumens der geschlossenen Position
               double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl);
               //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es ein teilweises Schließen
               if(volume_current>0 || order_close.VolumeCurrent()>0)
                 {
                  //--- Hinzufügen des Flags eines teilweisen Schließens
                  trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
                  //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es ein teilweises Schließen durch das Volumen einer entgegengesetzten Position
                  reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS);
                 }
               //--- Wenn das Auftragsvolumen vollständig geschlossen wurde, und es gibt ein teilweises Ausführen durch eine entgegengesetzte, gibt es ein teilweises Schließen mit dem Volumen der entgegengesetzten Position
               else
                 {
                  if(volume_opp_current>0)
                    {
                     reason=EVENT_REASON_DONE_BY_POS_PARTIALLY;
                    }
                 }
               CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID());
               if(event!=NULL)
                 {
                  event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                 // Ereigniszeit
                  event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON)
                  event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());              // Ereignis-Typ des Deals
                  event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // Deal-Ticket des Ereignisses
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder());       // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket());        // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());  // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());    // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());     // Auftragsticket auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag)
                  event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // Positions-ID
                  event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID());      // ID der entgegengesetzten Positions
                  event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // Magicnummer von Order/Deal/Position
                  event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                  // Ereignispreis
                  event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());             // Eröffnungspreis von Order/Deal/Position
                  event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());                 // Schließpreis von Order/Deal/Position
                  event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                // StopLoss (StopLoss des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());              // TakeProfit (TakeProfit des Positionsauftrags)
                  event.SetProperty(EVENT_PROP_VOLUME_INITIAL,::NormalizeDouble(volume_in,dgl));// Anfangsvolumen
                  event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume());                 // Schließvolumen
                  event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_current);                  // Verbliebenes (aktuelles) Volumen
                  event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull());                      // Gewinn
                  event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // Symbol des Auftrags
                  //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs
                  event.SetChartID(this.m_chart_id);
                  event.SetTypeEvent();
                  //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist
                  if(!this.IsPresentEventInList(event))
                    {
                     this.m_list_events.InsertSort(event);
                     //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses
                     event.SendEvent();
                     this.m_trade_event=event.TradeEvent();
                    }
                  //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt
                  else
                    {
                     ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
                     delete event;
                    }
                 }
              }
           }
         //--- Umkehrung
         else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT)
           {
            //--- Positionsumkehrung
            Print(DFUN,"Position reversal");
            order.Print();
           }
        }
     }
  }
//+------------------------------------------------------------------+

Die Methode erweist sich als recht lang. Daher werden alle Beschreibungen der notwendigen Prüfungen und entsprechenden Aktionen direkt im Code angegeben.

Die Methode überprüft den Status eines übergebenen Auftrags und alle notwendigen Komponenten eines aufgetretenen Ereignisses in Abhängigkeit von seiner Art (platzierte Pending-Order, entfernte Pending-Order, Deals). Ein neues Ereignis wird angelegt und mit Daten gefüllt, die dem Auftrag und dem Ereignistyp entsprechen, während das Ereignis in die Ereigniskollektion aufgenommen wird, und schließlich wird eine Nachricht über dieses Ereignis an die Regelprogrammkarte gesendet und die Variable, die den Typ des zuletzt aufgetretenen Ereignisses speichert, ausgefüllt.

Die Kollektion der Ereignisse ist fertig. Nun müssen wir sie in das Basisobjekt der Bibliothek einbinden.

Nach dem Erstellen der Klasse der Ereigniskollektion sind einige Dinge, die wir im vierten Teil der Basisobjektklasse CEngine zur Verfolgung von Events gemacht haben, redundant, daher sollte das Basisobjekt überarbeitet werden.

  1. Entfernen wir die 'private' Variable m_trade_event_code der Klasse, die den Statuscode des Trading-Events speichert.
  2. Entfernen wir die 'private' Methoden:
    1. SetTradeEvent() zum Dekodieren eines Ereigniscodes,
    2. IsTradeEventFlag() zur Rückgabe des Vorhandenseins eines Flags in einem Handelsereignis,
    3. WorkWithHedgeCollections() und WorkWithNettoCollections() zum Arbeiten mit Hedging- und Netting-Kollektionen und
    4. TradeEventCode() zur Rückgabe des Codes eines Handelsereignisses

Fügen wir das Einbinden der Klassendatei der Kollektion der Handelsereignisse dem Klassenkörper hinzu, deklarieren das Objekt der Ereigniskollektion, fügen die Methode TradeEventsControl() zum Arbeiten mit Ereignissen in den 'private' Teil der Klasse ein, ändern den Methodennamen GetListHistoryDeals() auf GetListDeals() im 'public' Abschnitt. Deals befinden sich immer in der historischen Kollektion, so dass ich glaube, es besteht keine Notwendigkeit, die Kollektion explizit im Methodennamen zu erwähnen. Ändern wir die Implementierung der Methode zum Zurücksetzen des letzten Handelsereignisses: Da wir nun das letzte Ereignis von der Event-Sammelklasse erhalten und die Methode zum Zurücksetzen des letzten Ereignisses innerhalb der Klasse vorhanden ist, müssen wir nur die gleichnamige Methode aus der Ereigniskollektion der Methode ResetLastTradeEvent() der Klasse aufrufen.

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Services\TimerCounter.mqh"
//+------------------------------------------------------------------+
//| Bibliothek der Basisklasse                                       |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Kollektion der historischen Aufträge und Deals
   CMarketCollection    m_market;                        // Kollektion der Marktorder und Deals
   CEventsCollection    m_events;                        // Kollektion der Ereignisse
   CArrayObj            m_list_counters;                 // Liste der Timerzähler
   bool                 m_first_start;                   // Flag des Erststarts
   bool                 m_is_hedge;                      // Flag des Hedging-Kontos
   bool                 m_is_market_trade_event;         // Flag eines Handelsereignisses des Kontos
   bool                 m_is_history_trade_event;        // Flag eines historischen Handelsereignisses auf dem Konto
   ENUM_TRADE_EVENT     m_acc_trade_event;               // Handelsereignis auf dem Konto
//--- Rückgabe des Zählerindex über die ID
   int                  CounterIndex(const int id) const;
//--- Rückgabe (1) des Flags des Erststarts, (2) der Existenz des Flags eines Handelsereignisses
   bool                 IsFirstStart(void);
//--- Arbeiten mit den Ereignissen
   void                 TradeEventsControl(void);
//--- Rückgabe der letzten (1) Pending-Order, (2) Marktorder, (3) letzten Position, (4) Position nach Ticket
   COrder*              GetLastMarketPending(void);
   COrder*              GetLastMarketOrder(void);
   COrder*              GetLastPosition(void);
   COrder*              GetPosition(const ulong ticket);
//--- Rückgabe der letzten (1) gelöschten Pending-Order, (2) historischen Marktorder, (3) historischen Aufträge (Markt- oder Pending-Orders) nach Ticket
   COrder*              GetLastHistoryPending(void);
   COrder*              GetLastHistoryOrder(void);
   COrder*              GetHistoryOrder(const ulong ticket);
//--- Rückgabe des (1) ersten und (2) des letzten historischen Marktorder aus der Liste aller Positionen, (3) des letzten Deals
   COrder*              GetFirstOrderPosition(const ulong position_id);
   COrder*              GetLastOrderPosition(const ulong position_id);
   COrder*              GetLastDeal(void);
public:
   //--- Rückgabe der Liste aller (1) Positionen, (2) Pending-Order und (3) Marktorders
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Rückgabe der Liste aller historischen (1) Aufträge, (2) gelöschten Pending-Orders, (3) Deals, (4) Positionen nach deren ID
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Rücksetzen des letzten Handelsereignisses
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Rückgabe des (1) Handelsereignisses und (2) Flag des Hedging-Kontos
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_acc_trade_event;       }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
//--- Erstellen des Timerzählers
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Timer
   void                 OnTimer(void);
//--- Constructor/Destructor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

Fügen wir im Konstruktor der Klasse CEngine die Behandlung des Millisekunden-Timer-Entwicklungsergebnisses hinzu. Wenn er nicht angelegt ist, zeigen wir eine entsprechende Meldung im Journal an. Als Nächstes werden wir die Klasse für die Behandlung bestimmter Fehler entwickeln, Flags setzen, die einem bibliotheksbasierten Programm sichtbar sind, und Fehlersituationen verarbeiten.

//+------------------------------------------------------------------+
//| CEngine Konstruktor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT)
  {
   ::ResetLastError();
   if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
      Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
  }
//+------------------------------------------------------------------+

Rufen wir im Timer der Klasse die Methode TradeEventsControl() auf, nachdem der Timer der Auftrags-, Deal- und Positionskollektion nicht unterbrochen wurde.

//+------------------------------------------------------------------+
//| CEngine Timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer der Kollektion der historischen Aufträge, Deals Marktorders und Positionen
   int index=this.CounterIndex(COLLECTION_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      //--- Wenn nicht 'in Pause', arbeite mit der Kollektion der Ereignisse
      if(counter!=NULL && counter.IsTimeDone())
        {
         this.TradeEventsControl();
        }
     }
  }
//+------------------------------------------------------------------+

Verbessern wir die Methode, die einen historischen Auftrag über das Ticket zurückgibt. Da die historische Kollektionsliste Pending-Orders, aktivierte Marktorders und Aufträge, die als Schließaufträge fungieren, beim Schließen durch eine entgegengesetzte Position enthalten kann, müssen wir alle Auftragstypen berücksichtigen.

Suchen wir dazu zunächst in der Liste der Markt- und Schließaufträge nach einem Auftrag mit dem Ticket. Wenn die Liste leer ist, suchen wir nach einer entfernten Pending-Order mit dem gleichen Ticket. Wenn auch diese Liste den Auftrag nicht enthält, wird NULL zurückgegeben. Andernfalls gibt das Programm das erste Element der sortierten Liste zurück. Wenn in der Liste kein Auftrag gefunden wurde, wird NULL zurückgegeben.

//+------------------------------------------------------------------+
//| Rückgabe historischer Aufträge nach dem Ticket                   |
//+------------------------------------------------------------------+
COrder* CEngine::GetHistoryOrder(const ulong ticket)
  {
   CArrayObj* list=this.GetListHistoryOrders();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL);
   if(list==NULL || list.Total()==0)
     {
      list=this.GetListHistoryPendings();
      list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL);
      if(list==NULL) return NULL;
     }
   COrder* order=list.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Implementieren wir die Methode TradeEventsControl() für die Arbeit mit Ereignissen auf dem Konto:

//+------------------------------------------------------------------+
//| Prüfen der Handelsereignisse                                     |
//+------------------------------------------------------------------+
void CEngine::TradeEventsControl(void)
  {
//--- Initialisieren der Codes und Flags der Handelsereignisse
   this.m_is_market_trade_event=false;
   this.m_is_history_trade_event=false;
//--- Aktualisieren der Liste 
   this.m_market.Refresh();
   this.m_history.Refresh();
//--- Aktionen beim ersten Start
   if(this.IsFirstStart())
     {
      this.m_acc_trade_event=TRADE_EVENT_NO_EVENT;
      return;
     }
//--- Prüfen der Änderungen des Marktzustands und der Kontohistorie 
   this.m_is_market_trade_event=this.m_market.IsTradeEvent();
   this.m_is_history_trade_event=this.m_history.IsTradeEvent();

//--- Im Falle irgendeines Ereignisses, werden die Listen, Flags und die Anzahl der neuen Aufträge und Deals an die Kollektion der Ereignisse gesendet und aktualisiert
   if(this.m_is_history_trade_event || this.m_is_market_trade_event)
     {
      this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(),
                            this.m_is_history_trade_event,this.m_is_market_trade_event,
                            this.m_history.NewOrders(),this.m_market.NewPendingOrders(),
                            this.m_market.NewMarketOrders(),this.m_history.NewDeals());
      //--- Abrufen des letzten Handelsereignisses auf dem Konto
      this.m_acc_trade_event=this.m_events.GetLastTradeEvent();
     }
  }

Diese Methode ist viel kürzer als ihr Vorgänger WorkWithHedgeCollections() aus dem vierten Teil der Bibliotheksbeschreibung.

Die Methode ist einfach und bedarf keiner Erklärungen. Der Code enthält alle Kommentare, so dass Sie seine einfache Logik verstehen können.

Hier ist der vollständige Code der aktualisierten Klasse CEngine:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include-Dateien                                                  |
//+------------------------------------------------------------------+
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Services\TimerCounter.mqh"
//+------------------------------------------------------------------+
//| Bibliothek der Basisklasse                                       |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Kollektion der historischen Aufträge und Deals
   CMarketCollection    m_market;                        // Kollektion der Marktorder und Deals
   CEventsCollection    m_events;                        // Kollektion der Ereignisse
   CArrayObj            m_list_counters;                 // Liste der Timerzähler
   bool                 m_first_start;                   // Flag des Erststarts
   bool                 m_is_hedge;                      // Flag des Hedging-Kontos
   bool                 m_is_market_trade_event;         // Flag eines Handelsereignisses des Kontos
   bool                 m_is_history_trade_event;        // Flag eines historischen Handelsereignisses auf dem Konto
   ENUM_TRADE_EVENT     m_acc_trade_event;               // Handelsereignis auf dem Konto
//--- Rückgabe des Zählerindex über die ID
   int                  CounterIndex(const int id) const;
//--- Rückgabe (1) des Flags des Erststarts, (2) der Existenz des Flags eines Handelsereignisses
   bool                 IsFirstStart(void);
//--- Arbeiten mit den Ereignissen
   void                 TradeEventsControl(void);
//--- Rückgabe der letzten (1) Pending-Order, (2) Marktorder, (3) letzten Position, (4) Position nach Ticket
   COrder*              GetLastMarketPending(void);
   COrder*              GetLastMarketOrder(void);
   COrder*              GetLastPosition(void);
   COrder*              GetPosition(const ulong ticket);
//--- Rückgabe der letzten (1) gelöschten Pending-Order, (2) historischen Marktorder, (3) historischen Aufträge (Markt- oder Pending-Orders) nach Ticket
   COrder*              GetLastHistoryPending(void);
   COrder*              GetLastHistoryOrder(void);
   COrder*              GetHistoryOrder(const ulong ticket);
//--- Rückgabe des (1) ersten und (2) des letzten historischen Marktorder aus der Liste aller Positionen, (3) des letzten Deals
   COrder*              GetFirstOrderPosition(const ulong position_id);
   COrder*              GetLastOrderPosition(const ulong position_id);
   COrder*              GetLastDeal(void);
public:
   //--- Rückgabe der Liste aller (1) Positionen, (2) Pending-Order und (3) Marktorders
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Rückgabe der Liste aller historischen (1) Aufträge, (2) gelöschten Pending-Orders, (3) Deals, (4) Positionen nach deren ID
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Rücksetzen des letzten Handelsereignisses
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Rückgabe des (1) Handelsereignisses und (2) Flag des Hedging-Kontos
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_acc_trade_event;       }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
//--- Erstellen des Timerzählers
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Timer
   void                 OnTimer(void);
//--- Constructor/Destructor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+
//| CEngine Konstruktor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT)
  {
   ::ResetLastError();
   if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
      Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
  }
//+------------------------------------------------------------------+
//| CEngine Destruktor                                               |
//+------------------------------------------------------------------+
CEngine::~CEngine()
  {
   ::EventKillTimer();
  }
//+------------------------------------------------------------------+
//| CEngine Timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer der Kollektion der historischen Aufträge, Deals Marktorders und Positionen
   int index=this.CounterIndex(COLLECTION_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      //--- Wenn nicht 'in Pause', arbeite mit der Kollektion der Ereignisse
      if(counter!=NULL && counter.IsTimeDone())
        {
         this.TradeEventsControl();
        }
     }
  }
//+------------------------------------------------------------------+
//| Erstellen des Timerzählers                                       |
//+------------------------------------------------------------------+
void CEngine::CreateCounter(const int id,const ulong step,const ulong pause)
  {
   if(this.CounterIndex(id)>WRONG_VALUE)
     {
      ::Print(TextByLanguage("Ошибка. Уже создан счётчик с идентификатором ","Error. Already created counter with id "),(string)id);
      return;
     }
   m_list_counters.Sort();
   CTimerCounter* counter=new CTimerCounter(id);
   if(counter==NULL)
      ::Print(TextByLanguage("Не удалось создать счётчик таймера ","Failed to create timer counter "),(string)id);
   counter.SetParams(step,pause);
   if(this.m_list_counters.Search(counter)==WRONG_VALUE)
      this.m_list_counters.Add(counter);
   else
     {
      string t1=TextByLanguage("Ошибка. Счётчик с идентификатором ","Error. Counter with ID ")+(string)id;
      string t2=TextByLanguage(", шагом ",", step ")+(string)step;
      string t3=TextByLanguage(" и паузой "," and pause ")+(string)pause;
      ::Print(t1+t2+t3+TextByLanguage(" уже существует"," already exists"));
      delete counter;
     }
  }
//+------------------------------------------------------------------+
//| Rückgabe des Zählerindex der Liste über die ID                   |
//+------------------------------------------------------------------+
int CEngine::CounterIndex(const int id) const
  {
   int total=this.m_list_counters.Total();
   for(int i=0;i<total;i++)
     {
      CTimerCounter* counter=this.m_list_counters.At(i);
      if(counter==NULL) continue;
      if(counter.Type()==id) 
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Rückgabe des Flags des ersten Starts, rücksetzen des Flags       |
//+------------------------------------------------------------------+
bool CEngine::IsFirstStart(void)
  {
   if(this.m_first_start)
     {
      this.m_first_start=false;
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Prüfen der Handelsereignisse                                     |
//+------------------------------------------------------------------+
void CEngine::TradeEventsControl(void)
  {
//--- Initialisieren des Codes und der Flags des Handelsereignisses
   this.m_is_market_trade_event=false;
   this.m_is_history_trade_event=false;
//--- Aktualisieren der Liste 
   this.m_market.Refresh();
   this.m_history.Refresh();
//--- Aktionen beim ersten Start
   if(this.IsFirstStart())
     {
      this.m_acc_trade_event=TRADE_EVENT_NO_EVENT;
      return;
     }
//--- Prüfen der Änderungen des Marktstatus' und der Kontohistorie 
   this.m_is_market_trade_event=this.m_market.IsTradeEvent();
   this.m_is_history_trade_event=this.m_history.IsTradeEvent();

//--- Im Falle irgendeines Ereignisses, werden die Listen, Flags und die Anzahl der neuen Aufträge und Deals an die Kollektion der Ereignisse gesendet und aktualisiert
   if(this.m_is_history_trade_event || this.m_is_market_trade_event)
     {
      this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(),
                            this.m_is_history_trade_event,this.m_is_market_trade_event,
                            this.m_history.NewOrders(),this.m_market.NewPendingOrders(),
                            this.m_market.NewMarketOrders(),this.m_history.NewDeals());
      //--- Abrufen des letzten Handelsereignisses auf dem Konto
      this.m_acc_trade_event=this.m_events.GetLastTradeEvent();
     }
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der Marktpositionen                           |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListMarketPosition(void)
  {
   CArrayObj* list=this.m_market.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der Pending-Orders                            |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListMarketPendings(void)
  {
   CArrayObj* list=this.m_market.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der Marktordersd                              |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListMarketOrders(void)
  {
   CArrayObj* list=this.m_market.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_ORDER,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der historischen Aufträge                     |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListHistoryOrders(void)
  {
   CArrayObj* list=this.m_history.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_ORDER,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der entfernten Pending-Orders                 |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListHistoryPendings(void)
  {
   CArrayObj* list=this.m_history.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der Liste der Deals                                     |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListDeals(void)
  {
   CArrayObj* list=this.m_history.GetList();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//|  Rückgabe der Liste aller Positionen                             |
//+------------------------------------------------------------------+
CArrayObj* CEngine::GetListAllOrdersByPosID(const ulong position_id)
  {
   CArrayObj* list=this.GetListHistoryOrders();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL);
   return list;
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten Position                                    |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastPosition(void)
  {
   CArrayObj* list=this.GetListMarketPosition();
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der Position nach der Ticketnummer                      |
//+------------------------------------------------------------------+
COrder* CEngine::GetPosition(const ulong ticket)
  {
   CArrayObj* list=this.GetListMarketPosition();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL);
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TICKET);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe des letzten Deals                                       |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastDeal(void)
  {
   CArrayObj* list=this.GetListDeals();
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten Pending-Order im Markt                      |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastMarketPending(void)
  {
   CArrayObj* list=this.GetListMarketPendings();
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten historischen Pending-Order                  |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastHistoryPending(void)
  {
   CArrayObj* list=this.GetListHistoryPendings();
   if(list==NULL) return NULL;
   list.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN_MSC #else SORT_BY_ORDER_TIME_CLOSE_MSC #endif);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten Marktorder                                  |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastMarketOrder(void)
  {
   CArrayObj* list=this.GetListMarketOrders();
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten historischen Marktorder                     |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastHistoryOrder(void)
  {
   CArrayObj* list=this.GetListHistoryOrders();
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe historischer Aufträge nach dem Ticket                   |
//+------------------------------------------------------------------+
COrder* CEngine::GetHistoryOrder(const ulong ticket)
  {
   CArrayObj* list=this.GetListHistoryOrders();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL);
   if(list==NULL || list.Total()==0)
     {
      list=this.GetListHistoryPendings();
      list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL);
      if(list==NULL) return NULL;
     }
   COrder* order=list.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der ersten historischen Marktorder                      |
//| aus der Liste aller Positionen                                   |
//+------------------------------------------------------------------+
COrder* CEngine::GetFirstOrderPosition(const ulong position_id)
  {
   CArrayObj* list=this.GetListAllOrdersByPosID(position_id);
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN);
   COrder* order=list.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+
//| Rückgabe der letzten historischen Marktorder                     |
//| aus der Liste aller Positionen                                   |
//+------------------------------------------------------------------+
COrder* CEngine::GetLastOrderPosition(const ulong position_id)
  {
   CArrayObj* list=this.GetListAllOrdersByPosID(position_id);
   if(list==NULL) return NULL;
   list.Sort(SORT_BY_ORDER_TIME_OPEN);
   COrder* order=list.At(list.Total()-1);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

Testen der Prozesse zum Definieren, Behandeln und Empfangen von Ereignissen

Jetzt können wir mit Ereignissen arbeiten. Es ist an der Zeit, den EA auf die Prüfung und Behandlung von Ereignisbeschreibungen vorzubereiten und diese dem Steuerungsprogramm zu übermitteln.

Erstellen wir im Terminalverzeichnis\MQL5\Experts\TestDoEasy den Ordner Part05 und kopieren den EA TestDoEasyPart04.mq5 des vorherigen Teils unter einem neuen Namen: TestDoEasyPart05.mq5.

Nun ändern wir die Ereignisbehandlung OnChartEvent(), um nutzerdefinierte Ereignisse zu empfangen:

//+------------------------------------------------------------------+
//| Funktion der Chart-Events                                        |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(MQLInfoInteger(MQL_TESTER))
      return;
   if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0)
     {
      PressButtonEvents(sparam);
     }
   if(id>=CHARTEVENT_CUSTOM)
     {
      ushort event=ushort(id-CHARTEVENT_CUSTOM);
      Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam);
     } 
  }
//+------------------------------------------------------------------+

Hier, wenn die Ereignis-ID die nutzerdefinierte Ereignis-ID überschreitet oder gleich ist, empfangen wir den Ereigniscode, der von der abgeleiteten Klasse CEvent aus der Bibliothek übergeben wird. Beim Senden eines nutzerdefinierten Ereignisses über die Funktion EventChartCustom() mit dem Funktionsparameter custom_event_id (derjenige, der unser Ereignis erhält), wird der Wert der Konstante CHARTEVENT_CUSTOM (gleich 1000) aus der Enumeration ENUM_CHART_EVENT zum Ereigniswert addiert. Um den Ereigniswert zurückzubekommen, müssen wir daher einfach den Wert CHARTEVENT_CUSTOM von der Ereignis-ID abziehen. Danach zeigen wir die Ereignisdaten im Journal des Terminal an.
Folgende Daten werden angezeigt: die ID ('wie besehen'), die Ereignisbeschreibung in Form des Wertes der Enumeration ENUM_TRADE_EVENT, der lparam-Wert, der den Auftrag oder das Positionsticket speichert, der dparam-Wert, der den Auftragspreis speichert, und der Sparam-Wert — dem Symbol eines Auftrags oder einer am Event teilnehmenden Position oder der Name der Kontowährung, falls es sich um eine Saldenoperation handelt.
Zum Beispiel:

2019.04.06 03:19:54.442 OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD

Außerdem müssen wir die für den Teilabschluss berechnete Losgröße korrigieren. In den Vorgängerversionen der Test-EAs war das falsch, da der Wert des nicht ausgeführten Positionsvolumens (VolumeCurrent()) für die Losgrößenberechnung verwendet wurde. Er ist beim Öffnen einer Position im Tester immer gleich Null, da der Tester keine Teilöffnungen simuliert. Dementsprechend wurde der minimale Wert der Losgröße zum Schließen herangezogen, da die Funktion für die Berechnung der Losgröße Null immer auf den kleinsten erlaubten Wert eingestellt hat.

Finden wir die Zeichenketten, in denen die Losgröße für den Teilabschluss berechnet wird und VolumeCurrent() durch Volume() ersetzen:

               //--- Berechnen des Schließvolumens und schließen der Hälfte der Kaufposition nach dem Ticket.
               trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));

               //--- Berechnen des Schließvolumens und schließen der Hälfte der Verkaufsposition nach dem Ticket.
               trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));

Nur zwei Stellen im Code — Schließen der Hälfte der Kaufposition und Schließen der Hälfte der Verkaufsposition.

Außerdem fügen wir Tastenverschiebung um die X- und Y-Achse zu den EA-Eingaben hinzu, um die Position der Schaltflächen auf dem Chart des visuellen Tester zu verbessern (ich habe die Tasten nach rechts verschoben, um den Auftrag und die Tickets der Position der Tickets im Visualizer zu sehen, da sie durch die Tasten ausgeblendet werden konnten):

//--- Eingabeparameter
input ulong    InpMagic       =  123;  // Magicnummer
input double   InpLots        =  0.1;  // Losgröße
input uint     InpStopLoss    =  50;   // StopLoss in Punkten
input uint     InpTakeProfit  =  50;   // TakeProfit in Punkten
input uint     InpDistance    =  50;   // Abstand der Pending-Orders (Punkte)
input uint     InpDistanceSL  =  50;   // Abstand von StopLimit-Orders (Punkte)
input uint     InpSlippage    =  0;    // Slippage in Punkten
input double   InpWithdrawal  =  10;   // Abbuchung von Geldern (im Tester)
input uint     InpButtShiftX  =  40;   // Versatz der Schaltfläche nach X 
input uint     InpButtShiftY  =  10;   // Versatz der Schaltfläche nach Y 
//--- Globale Variablen

Ändern wir noch leicht den Funktionscode für die Tastenbedienung:

//+------------------------------------------------------------------+
//| Erstellen des Panels mit Schaltflächen                           |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=30,const int shift_y=0)
  {
   int h=18,w=84,offset=2;
   int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-3) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+

und implementieren wir den Funktionsaufruf in der Funktion OnInit():

//--- Erstellen der Schaltflächen
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Setzen der Handelsparameter

Der vollständige Code des EA ist unten zusehen.

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart05.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/de/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/de/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
#include <Trade\Trade.mqh>
//--- Enumerationen
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_PROFIT_WITHDRAWAL
  };
#define TOTAL_BUTT   (17)
//--- Strukturen
struct SDataButt
  {
   string      name;
   string      text;
  };
//--- Eingabeparameter
input ulong    InpMagic       =  123;  // Magicnummer
input double   InpLots        =  0.1;  // Losgröße
input uint     InpStopLoss    =  50;   // StopLoss in Punkten
input uint     InpTakeProfit  =  50;   // TakeProfit in Punkten
input uint     InpDistance    =  50;   // Abstand der Pending-Orders (Punkte)
input uint     InpDistanceSL  =  50;   // Abstand von StopLimit-Orders (Punkte)
input uint     InpSlippage    =  0;    // Slippage in Punkten
input double   InpWithdrawal  =  10;   // Abbuchung von Geldern (im Tester)
input uint     InpButtShiftX  =  40;   // Versatz der Schaltfläche nach X 
input uint     InpButtShiftY  =  10;   // Versatz der Schaltfläche nach Y 
//--- Globale Variablen
CEngine        engine;
CTrade         trade;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Prüfen des Kontotyps
   if(!engine.IsHedge())
     {
      Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge"));
      return INIT_FAILED;
     }
//--- Setzen der globalen Variablen
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
//--- Erstellen der Schaltflächen
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Setzen der Handelsparameter
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialisierungsfunktion des Experten                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Lösche Objekte
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      int total=ObjectsTotal(0);
      for(int i=0;i<total;i++)
        {
         string obj_name=ObjectName(0,i);
         if(StringFind(obj_name,prefix+"BUTT_")<0)
            continue;
         PressButtonEvents(obj_name);
        }
     }
   if(engine.LastTradeEvent()!=last_event)
     {
      Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent()));
      last_event=engine.LastTradeEvent();
     }
  }
//+------------------------------------------------------------------+
//| Timer Funktion                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(!MQLInfoInteger(MQL_TESTER))
      engine.OnTimer();
  }
//+------------------------------------------------------------------+
//| Funktion der Chart-Events                                        |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(MQLInfoInteger(MQL_TESTER))
      return;
   if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0)
     {
      PressButtonEvents(sparam);
     }
   if(id>=CHARTEVENT_CUSTOM)
     {
      ushort event=ushort(id-CHARTEVENT_CUSTOM);
      Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam);
     } 
  }
//+------------------------------------------------------------------+
//| Erstellen des Panels mit Schaltflächen                           |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=30,const int shift_y=0)
  {
   int h=18,w=84,offset=2;
   int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-3) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+
//| Erstellen der Schaltflächen                                      |
//+------------------------------------------------------------------+
bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8)
  {
   if(ObjectFind(0,name)<0)
     {
      if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) 
        { 
         Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); 
         return false; 
        } 
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
      ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(0,name,OBJPROP_XSIZE,w);
      ObjectSetInteger(0,name,OBJPROP_YSIZE,h);
      ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size);
      ObjectSetString(0,name,OBJPROP_FONT,font);
      ObjectSetString(0,name,OBJPROP_TEXT,text);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
      ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n");
      ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Rückgabe des Status der Schaltflächen                            |
//+------------------------------------------------------------------+
bool ButtonState(const string name)
  {
   return (bool)ObjectGetInteger(0,name,OBJPROP_STATE);
  }
//+------------------------------------------------------------------+
//| Setzen des Status' der Schaltflächen                             |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
  }
//+------------------------------------------------------------------+
//| Transformieren der Enumeration in den Text der Schaltflächen     |
//+------------------------------------------------------------------+
string EnumToButtText(const ENUM_BUTTONS member)
  {
   string txt=StringSubstr(EnumToString(member),5);
   StringToLower(txt);
   StringReplace(txt,"buy","Buy");
   StringReplace(txt,"sell","Sell");
   StringReplace(txt,"_limit"," Limit");
   StringReplace(txt,"_stop"," Stop");
   StringReplace(txt,"close_","Close ");
   StringReplace(txt,"2"," 1/2");
   StringReplace(txt,"_by_"," by ");
   StringReplace(txt,"profit_","Profit ");
   StringReplace(txt,"delete_","Delete ");
   return txt;
  }
//+------------------------------------------------------------------+
//| Bearbeiten des Klicks auf Schaltflächen                          |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   //--- Konvertieren der Namen der Schaltflächen in die Zeichenketten-ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Falls eine Taste gedrückt wurde
   if(ButtonState(button_name))
     {
      //--- Wenn die Schaltfläche BUTT_BUY geklickt wurde: Eröffnen einer Kaufposition
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit);
         //--- Eröffnen einer Kaufposition
         trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_BUY_LIMIT geklickt wurde: Setzen von BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit);
         //--- Setzen einer BuyLimit-Order
         trade.BuyLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP geklickt wurde: Platzieren von BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit);
         //--- Setzen einer BuyStop-Order
         trade.BuyStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_BUY_STOP_LIMIT geklickt wurde: Platzieren von BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Abrufen des Preises von BuyStop relativ zu StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Berechnen des Preises der BuyLimit-Order relativ zu BuyStop unter Berücksichtigung des StopLevels
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit);
         //--- Setzen von BuyStopLimit-Order
         trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- Wenn die Schaltfläche BUTT_SELL geklickt wurde: Eröffnen einer Verkaufsposition
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit);
         //--- Eröffnen einer Verkaufsposition
         trade.Sell(lot,Symbol(),0,sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_SELL_LIMIT geklickt wurde: Setzen von SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit);
         //--- Setzen von SellLimit-Order
         trade.SellLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP geklickt wurde: Platzieren von SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit);
         //--- Setzen von SellStop-Order
         trade.SellStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- Falls die Schaltfläche BUTT_SELL_STOP_LIMIT geklickt wurde: Platzieren von SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Abrufen des Preises von SellStop relativ zu StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Berechnen des Preises der SellLimit-Order relativ zu SellStop unter Berücksichtigung des StopLevels
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop);
         //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit);
         //--- Setzen der SellStopLimit-Order
         trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_BUY geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Auswählen von nur Kaufpositionen aus der Liste
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Kaufposition mit Maximalgewinn
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Abrufen der Ticketnummer der Kaufposition und Schließen der Position mittels der Ticketnummer
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_BUY2 geklickt wurde: Schließen der Hälfte des Kaufs mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Auswählen von nur Kaufpositionen aus der Liste
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Kaufposition mit Maximalgewinn
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Berechnen des zu schließenden Volumens und schließen der Hälfte der Kaufposition mittels der Ticketnummer
               trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_BUY_BY_SELL geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn durch einen entgegengesetzten Verkauf
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- Auswählen von nur Kaufpositionen aus der Liste
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Kaufposition mit Maximalgewinn
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- Auswählen von nur Verkaufspositionen aus der Liste
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
           {
            //--- Auswählen der Kaufposition mit Maximalgewinn
            COrder* position_buy=list_buy.At(index_buy);
            //--- Auswählen der Verkaufsposition mit Maximalgewinn
            COrder* position_sell=list_sell.At(index_sell);
            if(position_buy!=NULL && position_sell!=NULL)
              {
               //--- Schließen der Kaufposition durch eine entgegengesetzte Verkaufsposition
               trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_SELL geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Auswählen von nur Verkaufspositionen aus der Liste
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Abrufen der Ticketnummer der Verkaufsposition und Schließen der Position mittels der Ticketnummer
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_SELL2 geklickt wurde: Schließen der Hälfte der Verkaufsposition mit Maximalgewinn
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Auswählen von nur Verkaufspositionen aus der Liste
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Berechnen des zu schließenden Volumens und schließen der Hälfte der Verkaufsposition mittels der Ticketnummer
               trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_SELL_BY_BUY geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn durch einen entgegengesetzten Kauf
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- Auswählen von nur Verkaufspositionen aus der Liste
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- Auswählen von nur Kaufpositionen aus der Liste
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Abrufen des Index der Kaufposition mit Maximalgewinn
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
           {
            //--- Auswählen der Verkaufsposition mit Maximalgewinn
            COrder* position_sell=list_sell.At(index_sell);
            //--- Auswählen der Kaufposition mit Maximalgewinn
            COrder* position_buy=list_buy.At(index_buy);
            if(position_sell!=NULL && position_buy!=NULL)
              {
               //--- Schließen einer Verkaufsposition mit einer entgegengesetzten Kaufposition
               trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_CLOSE_ALL geklickt wurde: Schließen aller Positionen beginnend mit dem kleinsten Gewinn
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Abrufen der Liste aller offenen Positionen
         CArrayObj* list=engine.GetListMarketPosition();
         if(list!=NULL)
           {
            //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- In der Schleife aller Positionen mit dem geringsten Gewinn
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- Schließen jeder Position mittels der Ticketnummer
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_DELETE_PENDING geklickt wurde:: Entfernen der ersten Pending-Order
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Abrufen der Liste aller Aufträge
         CArrayObj* list=engine.GetListMarketPendings();
         if(list!=NULL)
           {
            //--- Sortieren der neuen Liste nach der Platzierungszeit
            list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
            int total=list.Total();
            //--- In der Schleife aller Positionen mit der größten Zeitspanne
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- Löschen der Order mach dem Ticket
               trade.OrderDelete(order.Ticket());
              }
           }
        }
      //--- Wenn die Schaltfläche BUTT_PROFIT_WITHDRAWAL geklickt wurde: Gelder vom Konto abbuchen
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- Wenn das Programm im Tester gestartet wurde
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Emulieren eine Kontoabbuchung
            TesterWithdrawal(withdrawal);
           }
        }
      //--- Warten für 1/10 einer Sekunde
      Sleep(100);
      //--- Klicken der Schaltfläche rückgängig machen und Neuzeichnen des Charts
      ButtonState(button_name,false);
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

Jetzt können wir den EA kompilieren und im Tester starten. Wenn wir auf die Schaltflächen klicken, werden im Testerjournal kurze zweizeilige Meldungen über auftretende Kontoereignisse angezeigt.


Einträge der Ereignisbehandlung des EAs werden im Journal nicht angezeigt, da sie außerhalb des Testers arbeiten. Wenn wir auf die Schaltflächen des EAs eines Demokontos klicken, werden im Terminaljournal drei Zeilen angezeigt: zwei Zeilen aus der Methode zur Anzeige von Kurznachrichten der Klasse CEvent und eine weitere — aus der Funktion OnChartEvent() des EAs.

Nachfolgend sehen wir ein Beispiel für die Anzeige einer Nachricht im Journal, wenn Platzieren und Entfernen einer Pending-Order:

- Pending-Order platziert: 2019.04.05 23:19:55.248 -                                                              
EURUSD 0.10 Sell Limit #375419507 at price 1.14562                                                             
OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD 
- Pending-Order entfernt: 2019.04.05 23:19:55.248 -                                                             
EURUSD 0.10 Sell Limit #375419507 at price 1.14562                                                             
OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=375419507, dparam=1.14562, sparam=EURUSD

Was kommt als Nächstes?

Im nächsten Artikel werden wir anfangen, die Funktionen für die Arbeit an MetaTrader 5 Netting-Konten hinzuzufügen.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie herunterladen und testen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept.