English Русский 中文 Español 日本語 Português
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil VII): Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Aufträgen und Positionen

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil VII): Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Aufträgen und Positionen

MetaTrader 5Beispiele | 15 Juli 2019, 08:47
5 561 0
Artyom Trishkin
Artyom Trishkin

Inhalt

Konzept

In den vorangegangenen Teilen, die sich mit der plattformübergreifenden Bibliothek für MetaTrader 5 und MetaTrader 4 befassen, haben wir Werkzeuge zur Erstellung von Funktionen für besondere Situationen entwickelt, die einen schnellen Zugriff von Programmen auf beliebige Daten zu beliebigen Aufträge und Positionen von Hedging- und Netting-Konten ermöglichen. Dies sind die Funktionen zur Verfolgung von Ereignissen, die bei Orders und Positionen auftreten — Platzieren, Entfernen und Aktivieren von offenen Orders sowie Öffnen und Schließen von Positionen.
Die Funktionen zur Verfolgung der Aktivierung der bereits platzierten StopLimit-Orders und der Änderung von Marktorders und -positionen ist jedoch noch nicht implementiert.

In diesem Artikel werden wir die Verfolgung des Aktivierungsereignisses der StopLimit-Order implementieren, das zur Platzierung einer Limit-Order führt.
Die Bibliothek verfolgt solche Ereignisse und sendet die notwendigen Nachrichten an das Programm, damit die Ereignisse weiterverwendet werden können.

Umsetzung

Beim Testen der Aktivierung von StopLimit-Orders habe ich festgestellt, dass sich dieses Ereignis nicht in der Kontovergangenheit widerspiegelt, d.h. es kann nicht einfach aus der Kontovergangenheit "wie besehen" entnommen werden. Daher müssen wir den Status bestehender Orders bis zu ihrer Änderung verfolgen (in unserem Fall bedeutet dies, dass wir die Art einer platzierten Order mit demselben Ticket ändern).

Ich werde die Implementierung des Verfolgens von aktivierten StopLimit-Order aus praktischer Sicht angehen. Neben der Entwicklung der erforderlichen Funktionen werde ich es auch andere Ereignisse durch Änderungen an bestehenden Orders und Positionen verfolgen lassen (Änderung des Preises bestehender Pending Orders, deren StopLoss und TakeProfit-Level sowie der gleichen Level, die zu offenen Positionen gehören).

Die Logik der vorbereiteten Funktionen ist wie folgt zu gestalten:

Wir haben Zugriff auf die vollständige Liste aller aktiven Orders und Positionen des Kontos. Die Liste ermöglicht es uns auch, den aktuellen Status jeder der Objekteigenschaften abzurufen. Um die Änderungen der überwachten Eigenschaften zu verfolgen, benötigen wir eine zusätzliche Liste mit dem "vergangenen" Zuständen der Eigenschaften, der zunächst gleich den aktuellen sind.
Beim Vergleich von Objekteigenschaften aus diesen beiden Listen gilt eine Eigenschaft als geändert, sobald eine Differenz in einer der überwachten Eigenschaften festgestellt wird. In diesem Fall wird sofort ein "geändertes" Objekt angelegt. Sowohl die Vergangenheit als auch die geänderte Eigenschaft werden in sie geschrieben, und das Objekt wird in die neue Liste — "die Liste der geänderten Objekte" — gestellt.
Diese Liste ist dann in der Klasse zu behandeln, die Kontoereignisse verfolgt.
Natürlich können wir ein Ereignis sofort nach dem Erkennen von Änderungen in den Objekteigenschaften senden, aber wir können eine Situation haben, in der mehrere Objekte in einem Tick geändert werden. Wenn wir die Änderungen sofort bearbeiten, können wir die Änderung nur des allerletzten Objekts aus dem Paket durchführen, was inakzeptabel ist. Das bedeutet, dass wir die Liste aller geänderten Objekte erstellen und die Größe der Liste in der Ereignisbehandlerklasse überprüfen sollten. Jedes geänderte Objekt aus der Liste der geänderten Objekte wird darin in einer Schleife behandelt. Dies verhindert, dass wir einen Teil der gleichzeitig aufgetretenen Veränderungen der Order- und Positionseigenschaften verlieren.

Bei der Erstellung der Kollektion von Marktorders und Positionen im dritten Teil der Bibliotheksbeschreibung haben wir uns entschieden, die Liste zu aktualisieren und die aktuelle und vorherige Hash-Summe zu speichern, die als Ticket+Positionsänderungszeit in Millisekunden und Volumen berechnet wurde. So können wir den aktuellen Status von Aufträgen und Positionen jederzeit verfolgen. Um Änderungen der Order- und Positionseigenschaften zu verfolgen, sind diese Daten jedoch für die Hash-Summenberechnung unzureichend.

  • Wir müssen diesen Preis berücksichtigen, um Preisänderungen der Order zu berücksichtigen.
  • Wir müssen auch diese Preise beachten, um die Preisänderungen von StopLoss und TakeProfit zu berücksichtigen.

Das bedeutet, dass wir diese drei Preise zur Hash-Summe addieren, aber jeder der Preise wird in eine siebenstellige ulong Zahl umgewandelt, indem einfach ein Dezimalpunkt entfernt und die Kapazität der Zahl um eine einzige Bestellung erhöht wird (um sechsstellige Angebote zu berücksichtigen). Wenn der Preis beispielsweise 1,12345 beträgt, ist der Hash-Summenwert 1123450.

Beginnen wir mit der Implementierung.

Fügen Sie Enumerationen mit den Flags für mögliche Positions- und Orderänderungen zusammen mit den Optionen selbst hinzu, die in der Datei Defines.mqh verfolgt werden sollen:

//+------------------------------------------------------------------+
//| List of flags of possible order and position change options      |
//+------------------------------------------------------------------+
enum ENUM_CHANGE_TYPE_FLAGS
  {
   CHANGE_TYPE_FLAG_NO_CHANGE    =  0,                      // No changes
   CHANGE_TYPE_FLAG_TYPE         =  1,                      // Order type change
   CHANGE_TYPE_FLAG_PRICE        =  2,                      // Price change
   CHANGE_TYPE_FLAG_STOP         =  4,                      // StopLoss change
   CHANGE_TYPE_FLAG_TAKE         =  8,                      // TakeProfit change
   CHANGE_TYPE_FLAG_ORDER        =  16                      // Order properties change flag
  };
//+------------------------------------------------------------------+
//| Possible order and position change options                       |
//+------------------------------------------------------------------+
enum ENUM_CHANGE_TYPE
  {
   CHANGE_TYPE_NO_CHANGE,                                   // No changes
   CHANGE_TYPE_ORDER_TYPE,                                  // Order type change
   CHANGE_TYPE_ORDER_PRICE,                                 // Order price change
   CHANGE_TYPE_ORDER_PRICE_STOP_LOSS,                       // Order and StopLoss price change 
   CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT,                     // Order and TakeProfit price change
   CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT,           // Order, StopLoss and TakeProfit price change
   CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT,                 // StopLoss and TakeProfit change
   CHANGE_TYPE_ORDER_STOP_LOSS,                             // Order's StopLoss change
   CHANGE_TYPE_ORDER_TAKE_PROFIT,                           // Order's TakeProfit change
   CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT,              // Change position's StopLoss and TakeProfit
   CHANGE_TYPE_POSITION_STOP_LOSS,                          // Change position's StopLoss
   CHANGE_TYPE_POSITION_TAKE_PROFIT,                        // Change position's TakeProfit
  };
//+------------------------------------------------------------------+

Was die Flags der möglichen Änderungsoptionen der Order- und Positionseigenschafts betrifft:

  • das Änderungsflag des Ordertyps wird beim Aktivieren einer StopLimit-Order gesetzt,
  • das Änderungsflag des Preises wird bei der Änderung des Preises einer Pending-Order gesetzt,
  • die Änderungsflags von StopLoss und TakeProfit sind selbsterklärend,
  • das Orderflag wird verwendet, um eine Änderung der Ordereigenschaft (nicht der Position) zu identifizieren.
Ich glaube, das Orderflag erfordert eine Klarstellung: Ordertyp und Preis können sich nur für ausstehende Orders eindeutig ändern (Positionsartenwechsel (Stornierung) auf einem Netting-Konto wird nicht berücksichtigt, da wir dessen Tracking im sechsten Teil der Bibliotheksbeschreibung implementiert haben), während die Preise von StopLoss und TakeProfit sowohl für Orders als auch für Positionen geändert werden können. Deshalb brauchen wir das Orderflag. Es ermöglicht uns, ein Ereignis genau zu definieren und den Ereignistyp an die Klasse der Ereignisverfolgung zu senden.

Die Enumeration aller möglichen Optionen zur Änderung von Aufträgen und Positionen enthält alle Optionen, die wir in Zukunft verfolgen werden. In diesem Artikel werden wir nur das Verfolgen des Aktivierungsereignis (CHANGE_TYPE_ORDER_TYPE) der StopLimit-Order implementieren.

Wir fügen acht neue Ereignisse (die während ihrer Identifizierung an das Programm gesendet werden sollen) der Enumeration ENUM_TRADE_EVENT für die Liste der möglichen Handelsgeschehnisse hinzu:

//+------------------------------------------------------------------+
//| Liste der möglichen Handelsereignisse auf dem Konto              |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // No trading event
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Pending-Order platziert
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Pending-Order entfernt
//--- Mitglieder der Enumeration stimmen mit den Mitgliedern der Enumeration ENUM_DEAL_TYPE überein
//--- (constant order below should not be changed, no constants should be added/deleted)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Accruing credit (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Weitere Lastschrift
   TRADE_EVENT_ACCOUNT_CORRECTION,                          // Korrekturbuchung
   TRADE_EVENT_ACCOUNT_BONUS,                               // Accruing bonuses
   TRADE_EVENT_ACCOUNT_COMISSION,                           // Zusätzliche Kommission
   TRADE_EVENT_ACCOUNT_COMISSION_DAILY,                     // Commission charged at the end of a trading day
   TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY,                   // Commission charged at the end of a trading month
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY,               // Kommission des Agenten belastet am Tagesende
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY,             // Agent commission charged at the end of a month
   TRADE_EVENT_ACCOUNT_INTEREST,                            // Accrued interest on free funds
   TRADE_EVENT_BUY_CANCELLED,                               // Stornierter Kauf-Deal
   TRADE_EVENT_SELL_CANCELLED,                              // Stornierter Verkaufs-Deal
   TRADE_EVENT_DIVIDENT,                                    // Accruing dividends
   TRADE_EVENT_DIVIDENT_FRANKED,                            // Accruing franked dividends
   TRADE_EVENT_TAX                        = DEAL_TAX,       // Tax
//--- constants related to the DEAL_TYPE_BALANCE deal type from the DEAL_TYPE_BALANCE enumeration
   TRADE_EVENT_ACCOUNT_BALANCE_REFILL     = DEAL_TAX+1,     // Replenishing account balance
   TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2,     // Withdrawing funds from an account
//--- Remaining possible trading events
//--- (constant order below can be changed, constants can be added/deleted)
   TRADE_EVENT_PENDING_ORDER_ACTIVATED    = DEAL_TAX+3,     // Pending order activated by price
   TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL,             // Pending-Order, teilweise ausgelöst durch den Preis
   TRADE_EVENT_POSITION_OPENED,                             // Position eröffnet
   TRADE_EVENT_POSITION_OPENED_PARTIAL,                     // Position teilweise eröffnet
   TRADE_EVENT_POSITION_CLOSED,                             // Position geschlossen
   TRADE_EVENT_POSITION_CLOSED_BY_POS,                      // Position closed partially
   TRADE_EVENT_POSITION_CLOSED_BY_SL,                       // Position geschlossen duch Stop-Loss
   TRADE_EVENT_POSITION_CLOSED_BY_TP,                       // Position geschlossen durch Take-Profit
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET,                 // Position reversal by a new deal (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING,                // Position reversal by activating a pending order (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL,         // Position reversal by partial market order execution (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL,        // Position reversal by partial pending order activation (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET,               // Added volume to a position by a new deal (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL,       // Added volume to a position by partial activation of an order (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING,              // Added volume to a position by activating a pending order (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL,      // Added volume to a position by partial activation of a pending order (netting)
   TRADE_EVENT_POSITION_CLOSED_PARTIAL,                     // Position teilweise geschlossen
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS,              // Position closed partially by an opposite one
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL,               // Position teilweise geschlossen duch Stop-Loss
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP,               // Position teilweise geschlossen durch Take-Profit
   TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER,                  // StopLimit order activation
   TRADE_EVENT_MODIFY_ORDER_PRICE,                          // Changing order price
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS,                // Changing order and StopLoss price 
   TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT,              // Changing order and TakeProfit price
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT,    // Changing order, StopLoss and TakeProfit price
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT,          // Changing order's StopLoss and TakeProfit price
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS,                   // Changing position StopLoss
   TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT,                 // Changing position TakeProfit
  };

Schließlich fügen wir die neue Konstante mit der Beschreibung der Aktivierung einer StopLimit-Order der Enumeration ENUM_EVENT_REASON für die Liste der Ereignisursachen hinzu:

//+------------------------------------------------------------------+
//| Ereignisgrund                                                    |
//+------------------------------------------------------------------+
enum ENUM_EVENT_REASON
  {
   EVENT_REASON_REVERSE,                                    // Position reversal (netting)
   EVENT_REASON_REVERSE_PARTIALLY,                          // Position reversal by partial request execution (netting)
   EVENT_REASON_REVERSE_BY_PENDING,                         // Position reversal by pending order activation (netting)
   EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY,               // Position reversal in case of a pending order partial execution (netting)
   //--- All constants related to a position reversal should be located in the above list
   EVENT_REASON_ACTIVATED_PENDING,                          // Pending order activation
   EVENT_REASON_ACTIVATED_PENDING_PARTIALLY,                // Pending order partial activation
   EVENT_REASON_STOPLIMIT_TRIGGERED,                        // StopLimit order activation
   EVENT_REASON_CANCEL,                                     // Cancelation
   EVENT_REASON_EXPIRED,                                    // Order expiration
   EVENT_REASON_DONE,                                       // Request executed in full
   EVENT_REASON_DONE_PARTIALLY,                             // Request executed partially
   EVENT_REASON_VOLUME_ADD,                                 // Add volume to a position (netting)
   EVENT_REASON_VOLUME_ADD_PARTIALLY,                       // Add volume to a position by a partial request execution (netting)
   EVENT_REASON_VOLUME_ADD_BY_PENDING,                      // Add volume to a position when a pending order is activated (netting)
   EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY,            // Add volume to a position when a pending order is partially executed (netting)
   EVENT_REASON_DONE_SL,                                    // Closing by StopLoss
   EVENT_REASON_DONE_SL_PARTIALLY,                          // Partial closing by StopLoss
   EVENT_REASON_DONE_TP,                                    // Closing by TakeProfit
   EVENT_REASON_DONE_TP_PARTIALLY,                          // Partial closing by TakeProfit
   EVENT_REASON_DONE_BY_POS,                                // Closing by an opposite position
   EVENT_REASON_DONE_PARTIALLY_BY_POS,                      // Partial closing by an opposite position
   EVENT_REASON_DONE_BY_POS_PARTIALLY,                      // Closing an opposite position by a partial volume
   EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY,            // Partial closing of an opposite position by a partial volume
   //--- Konstanten bezüglich des Dealtyps DEAL_TYPE_BALANCE aus der Enumeration ENUM_DEAL_TYPE
   EVENT_REASON_BALANCE_REFILL,                             // Refilling the balance
   EVENT_REASON_BALANCE_WITHDRAWAL,                         // Withdrawing funds from the account
   //--- 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,                             // Accruing credit
   EVENT_REASON_ACCOUNT_CHARGE,                             // Additional charges
   EVENT_REASON_ACCOUNT_CORRECTION,                         // Correcting entry
   EVENT_REASON_ACCOUNT_BONUS,                              // Accruing bonuses
   EVENT_REASON_ACCOUNT_COMISSION,                          // Additional commissions
   EVENT_REASON_ACCOUNT_COMISSION_DAILY,                    // Commission charged at the end of a trading day
   EVENT_REASON_ACCOUNT_COMISSION_MONTHLY,                  // Commission charged at the end of a trading month
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY,              // Agent commission charged at the end of a trading day
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY,            // Agent commission charged at the end of a month
   EVENT_REASON_ACCOUNT_INTEREST,                           // Accruing interest on free funds
   EVENT_REASON_BUY_CANCELLED,                              // Canceled buy deal
   EVENT_REASON_SELL_CANCELLED,                             // Canceled sell deal
   EVENT_REASON_DIVIDENT,                                   // Accruing dividends
   EVENT_REASON_DIVIDENT_FRANKED,                           // Accruing franked dividends
   EVENT_REASON_TAX                                         // Tax
  };
#define REASON_EVENT_SHIFT    (EVENT_REASON_ACCOUNT_CREDIT-3)

Wir haben alle Änderungen in der Datei Defines.mqh vorgenommen.

Da wir uns entschieden haben, die Liste der Kontrollaufträge zu erstellen und zu speichern, sollte diese Liste Objekte mit einem minimal ausreichenden Satz von Eigenschaften speichern, um den Moment zu definieren, in dem sich eine der Objekte der Marktorders oder Positionen ändert.

Lassen Sie uns die Objektklasse Kontrollauftrag anlegen.

Erstellen Sie die neue Klasse OrderControl.mqh im Ordner der Bibliothek Collections. Wir setzen die Klasse CObject der Standardbibliothek als Basis und binden die für die Klassenoperation notwendigen Dateien ein:

//+------------------------------------------------------------------+
//|                                                 OrderControl.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 "..\Defines.mqh"             
#include "..\Objects\Orders\Order.mqh"
#include <Object.mqh>                 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrderControl : public CObject
  {
private:

public:
                     COrderControl();
                    ~COrderControl();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrderControl::COrderControl()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrderControl::~COrderControl()
  {
  }
//+------------------------------------------------------------------+

Deklarieren wir sofort alle notwendigen Variablen und Methoden im privaten Bereich der Klasse:

private:
   ENUM_CHANGE_TYPE  m_changed_type;                                    // Order change type
   MqlTick           m_tick;                                            // Tick structure
   string            m_symbol;                                          // Symbol
   ulong             m_position_id;                                     // Position ID
   ulong             m_ticket;                                          // Order ticket
   long              m_magic;                                           // Magic number
   ulong             m_type_order;                                      // Order type
   ulong             m_type_order_prev;                                 // Previous order type
   double            m_price;                                           // Order price
   double            m_price_prev;                                      // Previous order price
   double            m_stop;                                            // StopLoss price
   double            m_stop_prev;                                       // Previous StopLoss price
   double            m_take;                                            // TakeProfit price
   double            m_take_prev;                                       // Previous TakeProfit price
   double            m_volume;                                          // Order volume
   datetime          m_time;                                            // Order placement time
   datetime          m_time_prev;                                       // Order previous placement time
   int               m_change_code;                                     // Order change code
//--- return the presence of the property change flag
   bool              IsPresentChangeFlag(const int change_flag)   const { return (this.m_change_code & change_flag)==change_flag;   }
//--- Return the order parameters change type
   void              CalculateChangedType(void);

Alle Variablen der Klasse haben klare Beschreibungen. Ich sollte eine Klarstellung bezüglich der Variablen zum Speichern der Tickstruktur vornehmen: Wenn eine StopLimit-Order aktiviert wird, müssen wir die Aktivierungszeit sichern. Die Zeit sollte in Millisekunden angegeben werden, während TimeCurrent() die Zeit ohne Millisekunden zurückgibt. Um die Zeit des letzten Ticks abzurufen, den ein Auftrag mit millisekundengenau aktiviert wurde, verwenden wir die Standardfunktion SymbolInfoTick(), die die Tickstruktur mit Daten füllt, einschließlich der Tickzeit in Millisekunden.

Der Auftragsänderungscode besteht aus Flags, die wir in der Enumeration ENUM_CHANGE_TYPE_FLAGS beschrieben haben und hängt von aufgetretenen Änderungen der Ordereigenschaften ab. Die im Folgenden beschriebene private Methode CalculateChangedType() prüft die Flags und erstellt den Orderänderungscode.

Ordnen Sie im Abschnitt 'public' der Klasse die Methoden zum Empfangen und Schreiben von Daten über den vorherigen und aktuellen Zustand der Eigenschaften der Kontrollorder an, die Methode, die den Typ der aufgetretenen Auftragseigenschaftenänderung, die Methode, die den neuen Status einer geänderten Order, die Methode, die den Typ einer aufgetretenen Änderung und die Methode, die die Änderung der Ordereigenschaften überprüft, zurückgibt, sowie das Setzen und Zurückgeben der aufgetretenen Änderungsart. Die Methode wird aus der Kollektionsklasse Marktorders und Positionen aufgerufen, um die Änderung von aktiven Orders und Positionen zu erkennen.

//+------------------------------------------------------------------+
//|                                                 OrderControl.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 "..\Defines.mqh"
#include "..\Objects\Orders\Order.mqh"
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Order and position control class                                 |
//+------------------------------------------------------------------+
class COrderControl : public CObject
  {
private:
   ENUM_CHANGE_TYPE  m_changed_type;                                    // Order change type
   MqlTick           m_tick;                                            // Tick structure
   string            m_symbol;                                          // Symbol
   ulong             m_position_id;                                     // Position ID
   ulong             m_ticket;                                          // Order ticket
   long              m_magic;                                           // Magic number
   ulong             m_type_order;                                      // Order type
   ulong             m_type_order_prev;                                 // Previous order type
   double            m_price;                                           // Order price
   double            m_price_prev;                                      // Previous order price
   double            m_stop;                                            // StopLoss price
   double            m_stop_prev;                                       // Previous StopLoss price
   double            m_take;                                            // TakeProfit price
   double            m_take_prev;                                       // Previous TakeProfit price
   double            m_volume;                                          // Order volume
   datetime          m_time;                                            // Order placement time
   datetime          m_time_prev;                                       // Order previous placement time
   int               m_change_code;                                     // Order change code
//--- return the presence of the property change flag
   bool              IsPresentChangeFlag(const int change_flag)   const { return (this.m_change_code & change_flag)==change_flag;   }
//--- Calculate the order parameters change type
   void              CalculateChangedType(void);
public:
//--- Set the (1,2) current and previous type (2,3) current and previous price, (4,5) current and previous StopLoss,
//--- (6,7) current and previous TakeProfit, (8,9) current and previous placement time, (10) volume
   void              SetTypeOrder(const ulong type)                     { this.m_type_order=type;        }
   void              SetTypeOrderPrev(const ulong type)                 { this.m_type_order_prev=type;   }
   void              SetPrice(const double price)                       { this.m_price=price;            }
   void              SetPricePrev(const double price)                   { this.m_price_prev=price;       }
   void              SetStopLoss(const double stop_loss)                { this.m_stop=stop_loss;         }
   void              SetStopLossPrev(const double stop_loss)            { this.m_stop_prev=stop_loss;    }
   void              SetTakeProfit(const double take_profit)            { this.m_take=take_profit;       }
   void              SetTakeProfitPrev(const double take_profit)        { this.m_take_prev=take_profit;  }
   void              SetTime(const datetime time)                       { this.m_time=time;              }
   void              SetTimePrev(const datetime time)                   { this.m_time_prev=time;         }
   void              SetVolume(const double volume)                     { this.m_volume=volume;          }
//--- Set (1) change type, (2) new current status
   void              SetChangedType(const ENUM_CHANGE_TYPE type)        { this.m_changed_type=type;      }
   void              SetNewState(COrder* order);
//--- Check and set order parameters change flags and return the change type
   ENUM_CHANGE_TYPE  ChangeControl(COrder* compared_order);

//--- Return (1,2,3,4) position ID, ticket, magic and symbol, (5,6) current and previous type (7,8) current and previous price,
//--- (9,10) current and previous StopLoss, (11,12) current and previous TakeProfit, (13,14) current and previous placement time, (15) volume
   ulong             PositionID(void)                             const { return this.m_position_id;     }
   ulong             Ticket(void)                                 const { return this.m_ticket;          }
   long              Magic(void)                                  const { return this.m_magic;           }
   string            Symbol(void)                                 const { return this.m_symbol;          }
   ulong             TypeOrder(void)                              const { return this.m_type_order;      }
   ulong             TypeOrderPrev(void)                          const { return this.m_type_order_prev; }
   double            Price(void)                                  const { return this.m_price;           }
   double            PricePrev(void)                              const { return this.m_price_prev;      }
   double            StopLoss(void)                               const { return this.m_stop;            }
   double            StopLossPrev(void)                           const { return this.m_stop_prev;       }
   double            TakeProfit(void)                             const { return this.m_take;            }
   double            TakeProfitPrev(void)                         const { return this.m_take_prev;       }
   ulong             Time(void)                                   const { return this.m_time;            }
   ulong             TimePrev(void)                               const { return this.m_time_prev;       }
   double            Volume(void)                                 const { return this.m_volume;          }
//--- Return the change type
   ENUM_CHANGE_TYPE  GetChangeType(void)                          const { return this.m_changed_type;    }

//--- Konstructor
                     COrderControl(const ulong position_id,const ulong ticket,const long magic,const string symbol) :
                                                                        m_change_code(CHANGE_TYPE_FLAG_NO_CHANGE),
                                                                        m_changed_type(CHANGE_TYPE_NO_CHANGE),
                                                                        m_position_id(position_id),m_symbol(symbol),m_ticket(ticket),m_magic(magic) {;}
  };
//+------------------------------------------------------------------+

Der Klassenkonstruktor erhält Positions-ID, Ticket, Magicnummer und das Symbol der Order/Position. In der Initialisierungsliste setzen wir die Auftragsänderungsflags und den aufgetretenen Änderungstyp zurück und schreiben die in den übergebenen Parametern erhaltenen Auftrags-/Positionsdaten sofort in die entsprechenden Mitgliedsvariablen der Klassen.

Implementierung der deklarierter Methoden außerhalb des Klassenkörpers.
Die private Methode, die den Typ der Order/Position-Parameteränderung berechnet:

//+------------------------------------------------------------------+
//| Calculate order parameters change type                           |
//+------------------------------------------------------------------+
void COrderControl::CalculateChangedType(void)
  {
   this.m_changed_type=
     (
      //--- If the order flag is set
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_ORDER) ? 
        (
         //--- If StopLimit order is activated
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TYPE)    ?  CHANGE_TYPE_ORDER_TYPE :
         //--- If an order price is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_PRICE)   ? 
           (
            //--- If StopLoss and TakeProfit are modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT :
            //--- If TakeProfit modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT : 
            //--- If StopLoss modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_PRICE_STOP_LOSS   :
            //--- Only order price is modified
            CHANGE_TYPE_ORDER_PRICE
           ) : 
         //--- Price is not modified
         //--- If StopLoss and TakeProfit are modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT :
         //--- If TakeProfit is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_ORDER_TAKE_PROFIT : 
         //--- If StopLoss is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_STOP_LOSS   :
         //--- No changes
         CHANGE_TYPE_NO_CHANGE
        ) : 
      //--- Position
      //--- If position's StopLoss and TakeProfit are modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT :
      //--- If position's TakeProfit is modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_POSITION_TAKE_PROFIT : 
      //--- If position's StopLoss is modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_POSITION_STOP_LOSS   :
      //--- No changes
      CHANGE_TYPE_NO_CHANGE
     );
  }
//+------------------------------------------------------------------+

Die Methode schreibt den Typ der aufgetretenen Änderung der zuvor deklarierten Enumeration ENUM_CHANGE_TYPE in die Klassenmitgliedervariable m_changed_type in Abhängigkeit vom Vorhandensein von Flags innerhalb der Variablen m_change_code.
Alle Aktionen im Zusammenhang mit dem Überprüfen von Flags sind in den Kommentaren zu der Methode beschrieben, die Zeichenketten auflistet und sollten leicht verständlich sein.
Die private Methode überprüft das Vorhandensein des Flags innerhalb der Variablen m_change_code.

bool IsPresentChangeFlag(const int change_flag const { return (this.m_change_code & change_flag)==change_flag }

Die Methode erhält das geprüft Flag. Das Vorhandensein innerhalb von m_change_code wird bitweise durch AND-Operation überprüft und das boolesche Ergebnis des Vergleichs (bitweise Operation zwischen dem Code und den Flag-Werten) mit dem geprüften Flag-Wert wird zurückgegeben.

Die Methode gibt einen neuen relevanten Status der Order-/Positionseigenschaften zurück:

//+------------------------------------------------------------------+
//| Set the new relevant status                                      |
//+------------------------------------------------------------------+
void COrderControl::SetNewState(COrder* order)
  {
   if(order==NULL || !::SymbolInfoTick(this.Symbol(),this.m_tick))
      return;
   //--- New type
   this.SetTypeOrderPrev(this.TypeOrder());
   this.SetTypeOrder(order.TypeOrder());
   //--- New price
   this.SetPricePrev(this.Price());
   this.SetPrice(order.PriceOpen());
   //--- New StopLoss
   this.SetStopLossPrev(this.StopLoss());
   this.SetStopLoss(order.StopLoss());
   //--- New TakeProfit
   this.SetTakeProfitPrev(this.TakeProfit());
   this.SetTakeProfit(order.TakeProfit());
   //--- New time
   this.SetTimePrev(this.Time());
   this.SetTime(this.m_tick.time_msc);
  }
//+------------------------------------------------------------------+

Der Zeiger auf die Order/Position, in der eine Änderung einer der Eigenschaften stattgefunden hat, wird an die Methode übergeben.
Sobald eine Änderung einer der Auftrags-/Positionseigenschaften erkannt wird, müssen wir den neuen Status für weitere Prüfungen speichern, die Methode speichert zuerst ihren aktuellen Eigenschaftsstatus als vorhergehende und schreibt den Eigenschaftswert aus der an die Methode übergebenen Bestellung als ihren aktuellen Status.
Wenn die Zeit eines aufgetretenen Ereignisses speichert, verwenden wir die Standardfunktion SymbolInfoTick(), um Tickzeit in Millisekunden zu empfangen.

Die Hauptmethode, die aus der Klasse CMarketCollection aufgerufen wird und aufgetretene Änderungen definiert:

//+------------------------------------------------------------------+
//| Check and set order parameters change flags                      |
//+------------------------------------------------------------------+
ENUM_CHANGE_TYPE COrderControl::ChangeControl(COrder *compared_order)
  {
   this.m_change_code=CHANGE_TYPE_FLAG_NO_CHANGE;
   if(compared_order==NULL || compared_order.Ticket()!=this.m_ticket)
      return CHANGE_TYPE_NO_CHANGE;
   if(compared_order.Status()==ORDER_STATUS_MARKET_ORDER || compared_order.Status()==ORDER_STATUS_MARKET_PENDING)
      this.m_change_code+=CHANGE_TYPE_FLAG_ORDER;
   if(compared_order.TypeOrder()!=this.m_type_order)
      this.m_change_code+=CHANGE_TYPE_FLAG_TYPE;
   if(compared_order.PriceOpen()!=this.m_price)
      this.m_change_code+=CHANGE_TYPE_FLAG_PRICE;
   if(compared_order.StopLoss()!=this.m_stop)
      this.m_change_code+=CHANGE_TYPE_FLAG_STOP;
   if(compared_order.TakeProfit()!=this.m_take)
      this.m_change_code+=CHANGE_TYPE_FLAG_TAKE;
   this.CalculateChangedType();
   return this.GetChangeType();
  }
//+------------------------------------------------------------------+

Das Verfahren empfängt den Zeiger auf die geprüfte Order/Position und initialisiert den Änderungscode. Wenn ein leeres Objekt einer verglichenen Order übergeben wird oder ihr Ticket nicht gleich dem Ticket des aktuellen Kontrollorder ist, geben wir den Änderungs-Abwesenheitscode zurück.

Überprüfen wir dann alle verfolgten Eigenschaften von Kontroll- und Kontrollaufträgen. Wird eine Abweichung festgestellt, wird dem Änderungscode das notwendige Kennzeichen hinzugefügt, das diese Änderung beschreibt.
Anschließend wird die Änderungsart durch den vollständig gebildeten Änderungscode in der Methode CalculateChangedType() berechnet und über die Methode GetChangeType() an das aufrufende Programm zurückgegeben.

Die vollständige Auflistung der Klasse der Kontrollorder:

//+------------------------------------------------------------------+
//|                                                 OrderControl.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 "..\Defines.mqh"
#include "..\Objects\Orders\Order.mqh"
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Order and position control class                                 |
//+------------------------------------------------------------------+
class COrderControl : public CObject
  {
private:
   ENUM_CHANGE_TYPE  m_changed_type;                                    // Order change type
   MqlTick           m_tick;                                            // Tick structure
   string            m_symbol;                                          // Symbol
   ulong             m_position_id;                                     // Position ID
   ulong             m_ticket;                                          // Order ticket
   long              m_magic;                                           // Magic number
   ulong             m_type_order;                                      // Order type
   ulong             m_type_order_prev;                                 // Previous order type
   double            m_price;                                           // Order price
   double            m_price_prev;                                      // Previous order price
   double            m_stop;                                            // StopLoss price
   double            m_stop_prev;                                       // Previous StopLoss price
   double            m_take;                                            // TakeProfit price
   double            m_take_prev;                                       // Previous TakeProfit price
   double            m_volume;                                          // Order volume
   datetime          m_time;                                            // Order placement time
   datetime          m_time_prev;                                       // Order previous placement time
   int               m_change_code;                                     // Order change code
//--- return the presence of the property change flag
   bool              IsPresentChangeFlag(const int change_flag)   const { return (this.m_change_code & change_flag)==change_flag;   }
//--- Calculate the order parameters change type
   void              CalculateChangedType(void);
public:
//--- Set the (1,2) current and previous type (2,3) current and previous price, (4,5) current and previous StopLoss,
//--- (6,7) current and previous TakeProfit, (8,9) current and previous placement time, (10) volume
   void              SetTypeOrder(const ulong type)                     { this.m_type_order=type;        }
   void              SetTypeOrderPrev(const ulong type)                 { this.m_type_order_prev=type;   }
   void              SetPrice(const double price)                       { this.m_price=price;            }
   void              SetPricePrev(const double price)                   { this.m_price_prev=price;       }
   void              SetStopLoss(const double stop_loss)                { this.m_stop=stop_loss;         }
   void              SetStopLossPrev(const double stop_loss)            { this.m_stop_prev=stop_loss;    }
   void              SetTakeProfit(const double take_profit)            { this.m_take=take_profit;       }
   void              SetTakeProfitPrev(const double take_profit)        { this.m_take_prev=take_profit;  }
   void              SetTime(const datetime time)                       { this.m_time=time;              }
   void              SetTimePrev(const datetime time)                   { this.m_time_prev=time;         }
   void              SetVolume(const double volume)                     { this.m_volume=volume;          }
//--- Set (1) change type, (2) new current status
   void              SetChangedType(const ENUM_CHANGE_TYPE type)        { this.m_changed_type=type;      }
   void              SetNewState(COrder* order);
//--- Check and set order parameters change flags and return the change type
   ENUM_CHANGE_TYPE  ChangeControl(COrder* compared_order);

//--- Return (1,2,3,4) position ID, ticket, magic and symbol, (5,6) current and previous type (7,8) current and previous price,
//--- (9,10) current and previous StopLoss, (11,12) current and previous TakeProfit, (13,14) current and previous placement time, (15) volume
   ulong             PositionID(void)                             const { return this.m_position_id;     }
   ulong             Ticket(void)                                 const { return this.m_ticket;          }
   long              Magic(void)                                  const { return this.m_magic;           }
   string            Symbol(void)                                 const { return this.m_symbol;          }
   ulong             TypeOrder(void)                              const { return this.m_type_order;      }
   ulong             TypeOrderPrev(void)                          const { return this.m_type_order_prev; }
   double            Price(void)                                  const { return this.m_price;           }
   double            PricePrev(void)                              const { return this.m_price_prev;      }
   double            StopLoss(void)                               const { return this.m_stop;            }
   double            StopLossPrev(void)                           const { return this.m_stop_prev;       }
   double            TakeProfit(void)                             const { return this.m_take;            }
   double            TakeProfitPrev(void)                         const { return this.m_take_prev;       }
   ulong             Time(void)                                   const { return this.m_time;            }
   ulong             TimePrev(void)                               const { return this.m_time_prev;       }
   double            Volume(void)                                 const { return this.m_volume;          }
//--- Return the change type
   ENUM_CHANGE_TYPE  GetChangeType(void)                          const { return this.m_changed_type;    }

//--- Konstructor
                     COrderControl(const ulong position_id,const ulong ticket,const long magic,const string symbol) :
                                                                        m_change_code(CHANGE_TYPE_FLAG_NO_CHANGE),
                                                                        m_changed_type(CHANGE_TYPE_NO_CHANGE),
                                                                        m_position_id(position_id),m_symbol(symbol),m_ticket(ticket),m_magic(magic) {;}
  };
//+------------------------------------------------------------------+
//| Check and set the order parameters change flags                  |
//+------------------------------------------------------------------+
ENUM_CHANGE_TYPE COrderControl::ChangeControl(COrder *compared_order)
  {
   this.m_change_code=CHANGE_TYPE_FLAG_NO_CHANGE;
   if(compared_order==NULL || compared_order.Ticket()!=this.m_ticket)
      return CHANGE_TYPE_NO_CHANGE;
   if(compared_order.Status()==ORDER_STATUS_MARKET_ORDER || compared_order.Status()==ORDER_STATUS_MARKET_PENDING)
      this.m_change_code+=CHANGE_TYPE_FLAG_ORDER;
   if(compared_order.TypeOrder()!=this.m_type_order)
      this.m_change_code+=CHANGE_TYPE_FLAG_TYPE;
   if(compared_order.PriceOpen()!=this.m_price)
      this.m_change_code+=CHANGE_TYPE_FLAG_PRICE;
   if(compared_order.StopLoss()!=this.m_stop)
      this.m_change_code+=CHANGE_TYPE_FLAG_STOP;
   if(compared_order.TakeProfit()!=this.m_take)
      this.m_change_code+=CHANGE_TYPE_FLAG_TAKE;
   this.CalculateChangedType();
   return this.GetChangeType();
  }
//+------------------------------------------------------------------+
//| Calculate the order parameters change type                       |
//+------------------------------------------------------------------+
void COrderControl::CalculateChangedType(void)
  {
   this.m_changed_type=
     (
      //--- If the order flag is set
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_ORDER) ? 
        (
         //--- If StopLimit order is activated
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TYPE)    ?  CHANGE_TYPE_ORDER_TYPE :
         //--- If an order price is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_PRICE)   ? 
           (
            //--- If StopLoss and TakeProfit are modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT :
            //--- If TakeProfit modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT : 
            //--- If StopLoss modified together with the price
            this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_PRICE_STOP_LOSS   :
            //--- Only order price is modified
            CHANGE_TYPE_ORDER_PRICE
           ) : 
         //--- Price is not modified
         //--- If StopLoss and TakeProfit are modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT :
         //--- If TakeProfit is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_ORDER_TAKE_PROFIT : 
         //--- If StopLoss is modified
         this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_ORDER_STOP_LOSS   :
         //--- No changes
         CHANGE_TYPE_NO_CHANGE
        ) : 
      //--- Position
      //--- If position's StopLoss and TakeProfit are modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) && this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT :
      //--- If position's TakeProfit is modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_TAKE) ? CHANGE_TYPE_POSITION_TAKE_PROFIT : 
      //--- If position's StopLoss is modified
      this.IsPresentChangeFlag(CHANGE_TYPE_FLAG_STOP) ? CHANGE_TYPE_POSITION_STOP_LOSS   :
      //--- No changes
      CHANGE_TYPE_NO_CHANGE
     );
  }
//+------------------------------------------------------------------+
//| Set the new relevant status                                      |
//+------------------------------------------------------------------+
void COrderControl::SetNewState(COrder* order)
  {
   if(order==NULL || !::SymbolInfoTick(this.Symbol(),this.m_tick))
      return;
   //--- New type
   this.SetTypeOrderPrev(this.TypeOrder());
   this.SetTypeOrder(order.TypeOrder());
   //--- New price
   this.SetPricePrev(this.Price());
   this.SetPrice(order.PriceOpen());
   //--- New StopLoss
   this.SetStopLossPrev(this.StopLoss());
   this.SetStopLoss(order.StopLoss());
   //--- New TakeProfit
   this.SetTakeProfitPrev(this.TakeProfit());
   this.SetTakeProfit(order.TakeProfit());
   //--- New time
   this.SetTimePrev(this.Time());
   this.SetTime(this.m_tick.time_msc);
  }
//+------------------------------------------------------------------+


Lassen Sie uns die Klasse der Kollektionen CMarketCollection für Marktorders und Positionen verbessern.

Wir müssen die Änderungen der Eigenschaften bei aktiven Aufträgen und Positionen verfolgen. Da wir alle Marktorders und Positionen dieser Klasse erhalten, wäre es sinnvoll, auch deren Modifikation darin zu überprüfen.

Binden wir die Klassendatei der Kontrollaufträge ein. Deklarieren wir im Abschnitt 'privet' der Klasse die Liste zum Speichern von Kontrollaufträgen und -positionen, Liste zum Speichern geänderter Aufträge und Positionen, Klassenmitgliedervariable zum Speichern der Auftragsänderungsart und Variable zum Speichern des Verhältnisses zur Umwandlung des Preises in die Hash-Summe.
Wir deklarieren auch die 'prtivate' Methoden:
Das Verfahren zum Umwandeln der Ordereigenschaften in eine Hash-Summe, das Verfahren zum Hinzufügen einer Order oder einer Position zur Liste der offenen Orders und Positionen auf dem Konto, das Verfahren zum Erstellen und Hinzufügen einer Kontrollorder zur Liste der Kontrollorder und das Verfahren zum Erstellen und Hinzufügen einer geänderten Order zur Liste der geänderten Orders, das Verfahren zum Entfernen eines Auftrags aus der Liste der Kontrollaufträge durch ein Ticket und eine Positions-ID, das Verfahren zum Rückgeben des Kontrollauftragsindex in der Liste der Kontrollaufträge durch ein Ticket und eine Positions-ID und dem Behandeln eines bestehenden Auftrags-/Positionsänderungsereignisses.

Wir deklarieren im 'public' Bereich der Klasse die Methode , die die erstellte Liste der geänderten Aufträge zurückgibt.

//+------------------------------------------------------------------+
//|                                             MarketCollection.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\MarketOrder.mqh"
#include "..\Objects\Orders\MarketPending.mqh"
#include "..\Objects\Orders\MarketPosition.mqh"
#include "OrderControl.mqh"
//+------------------------------------------------------------------+
//| Kollektion der Marktorders und Positionen                        |
//+------------------------------------------------------------------+
class CMarketCollection : public CListObj
  {
private:
   struct MqlDataCollection
     {
      ulong          hash_sum_acc;           // Hash sum of all orders and positions on the account
      int            total_market;           // Number of market orders on the account
      int            total_pending;          // Anzahl aller Pending-Order auf dem Konto
      int            total_positions;        // Anzahl der Positionen auf dem Konto
      double         total_volumes;          // Gesamtvolumen der Aufträge und Positionen auf dem Konto
     };
   MqlDataCollection m_struct_curr_market;   // Aktuelle Daten der Marktorder und Positionen auf dem Konto
   MqlDataCollection m_struct_prev_market;   // Vorherige Daten der Marktorder und Positionen auf dem Konto
   CListObj          m_list_all_orders;      // List of pending orders and positions on the account
   CArrayObj         m_list_control;         // List of control orders
   CArrayObj         m_list_changed;         // List of changed orders
   COrder            m_order_instance;       // Order object for searching by property
   ENUM_CHANGE_TYPE  m_change_type;          // Order change type
   bool              m_is_trade_event;       // Flag des Handelsereignisses
   bool              m_is_change_volume;     // Flag für die Änderung des Gesamtvolumens
   double            m_change_volume_value;  // Wert der Änderung des Gesamtvolumens
   ulong             m_k_pow;                // Ratio for converting the price into a hash sum
   int               m_new_market_orders;    // Number of new market orders
   int               m_new_positions;        // Number of new positions
   int               m_new_pendings;         // Anzahl der neuen Pending-Order
//--- Sichern der aktuellen Werte des Status der Kontodaten als die vorherigen
   void              SavePrevValues(void)                                                                { this.m_struct_prev_market=this.m_struct_curr_market;                  }
//--- Convert order data into a hash sum value
   ulong             ConvertToHS(COrder* order) const;
//--- Add an order or a position to the list of pending orders and positions on an account and sets the data on market orders and positions on the account
   bool              AddToListMarket(COrder* order);
//--- (1) Create and add a control order to the list of control orders, (2) a control order to the list of changed control orders
   bool              AddToListControl(COrder* order);
   bool              AddToListChanges(COrderControl* order_control);
//--- Remove an order by a ticket or a position ID from the list of control orders
   bool              DeleteOrderFromListControl(const ulong ticket,const ulong id);
//--- Return the control order index in the list by a position ticket and ID
   int               IndexControlOrder(const ulong ticket,const ulong id);
//--- Handler of an existing order/position change event
   void              OnChangeEvent(COrder* order,const int index);
public:
//--- Return the list of (1) all pending orders and open positions, (2) modified orders and positions
   CArrayObj*        GetList(void)                                                                       { return &this.m_list_all_orders;                                       }
   CArrayObj*        GetListChanges(void)                                                                { return &this.m_list_changed;                                          }
//--- Rückgabe der Liste von Aufträgen und Positionen mit einer Eröffnungszeit vom begin_time bis end_time
   CArrayObj*        GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Rückgabe der Liste von Aufträgen und Positionen ausgewählt nach (1) Double-, (2) Integer- und (3) String-Eigenschaften, die eoinem Vergleichswert entsprechen
   CArrayObj*        GetList(ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
   CArrayObj*        GetList(ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
   CArrayObj*        GetList(ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
//--- Return the number of (1) new market order, (2) new pending orders, (3) new positions, (4) occurred trading event flag, (5) changed volume
   int               NewMarketOrders(void)                                                         const { return this.m_new_market_orders;                                      }
   int               NewPendingOrders(void)                                                        const { return this.m_new_pendings;                                           }
   int               NewPositions(void)                                                            const { return this.m_new_positions;                                          }
   bool              IsTradeEvent(void)                                                            const { return this.m_is_trade_event;                                         }
   double            ChangedVolumeValue(void)                                                      const { return this.m_change_volume_value;                                    }
//--- Konstructor
                     CMarketCollection(void);
//--- Aktualisieren der Liste der Pending-Orders und Positionen
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Fügen wir dem Klassenkonstruktor das Löschen und Sortieren der Liste der Kontrollaufträge und der Liste der geänderten Orders sowie die Berechnung des Verhältnisses zur Definition der Hash-Summe hinzu:

//+------------------------------------------------------------------+
//| 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);
   this.m_list_control.Clear();  
   this.m_list_control.Sort();   
   this.m_list_changed.Clear();  
   this.m_list_changed.Sort();   
   this.m_k_pow=(ulong)pow(10,6);
  }
//+------------------------------------------------------------------+

Das Verfahren zum Umwandeln von Ordereigenschaften in die Zahl zur Berechnung der Hash-Summe:

//+---------------------------------------------------------------------+
//| Convert the order price and its type into a number for the hash sum |
//+---------------------------------------------------------------------+
ulong CMarketCollection::ConvertToHS(COrder *order) const
  {
   if(order==NULL)
      return 0;
   ulong price=ulong(order.PriceOpen()*this.m_k_pow);
   ulong stop=ulong(order.StopLoss()*this.m_k_pow);
   ulong take=ulong(order.TakeProfit()*this.m_k_pow);
   ulong type=order.TypeOrder();
   ulong ticket=order.Ticket();
   return price+stop+take+type+ticket;
  }
//+------------------------------------------------------------------+

Das Verfahren empfängt einen Zeiger auf die Order, deren Daten in eine Zahl umgewandelt werden sollen. Dann werden die Doppeleigenschaften der Order durch eine einfache Multiplikation mit dem zuvor im Klassenkonstruktor berechneten Verhältnis in eine Zahl für die Hash-Summe umgewandelt, alle Eigenschaftswerte werden summiert und als ulong Zahl zurückgegeben.

Die Methode zur Aktualisierung der aktuellen Daten des Marktumfelds Refresh() wurde in der Klasse verbessert. Wir haben es im dritten Teil der Bibliotheksbeschreibung beschrieben.

Die Änderungen betrafen das Hinzufügen von Objekten zur Liste der Aufträge und Positionen. Nun befinden sich diese gleichartigen Zeichenketten in der einzigen Methode AddToListMarket(). Nach der Deklaration eines Auftragsobjekts in der Liste der Aufträge und Positionen wird in der Liste der Kontrollaufträge das Vorhandensein desselben Auftrags überprüft. Wenn ein solcher Auftrag nicht vorhanden ist, wird eine Kontrollorder erstellt und mit der Methode AddToListControl() in die Liste der Kontrollorders aufgenommen. Wenn die Kontrollorder vorhanden ist, wird die Methode OnChangeEvent() zum Vergleichen der aktuellen Auftragseigenschaften mit denen der Kontrollorder aufgerufen.

Alle ausgeführten Aktionen werden durch Kommentare im Code beschrieben und hervorgehoben.

//+------------------------------------------------------------------+
//| Update the list of orders                                        |
//+------------------------------------------------------------------+
void CMarketCollection::Refresh(void)
  {
   ::ZeroMemory(this.m_struct_curr_market);
   this.m_is_trade_event=false;
   this.m_is_change_volume=false;
   this.m_new_pendings=0;
   this.m_new_positions=0;
   this.m_change_volume_value=0;
   this.m_list_all_orders.Clear();
#ifdef __MQL4__
   int total=::OrdersTotal();
   for(int i=0; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS)) continue;
      long ticket=::OrderTicket();
      //--- Get the control order index by a position ticket and ID
      int index=this.IndexControlOrder(ticket);
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         CMarketPosition *position=new CMarketPosition(ticket);
         if(position==NULL) continue;
         //--- Add a position object to the list of market orders and positions
         if(!this.AddToListMarket(position))
            continue;
         //--- If there is no order in the list of control orders and positions, add it
         if(index==WRONG_VALUE)
           {
            if(!this.AddToListControl(order))
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Не удалось добавить контрольный ордер ","Failed to add control order "),order.TypeDescription()," #",order.Ticket());
              }
           }
         //--- If the order is already present in the list of control orders, check it for changed properties
         if(index>WRONG_VALUE)
           {
            this.OnChangeEvent(position,index);
           }
        }
      else
        {
         CMarketPending *order=new CMarketPending(ticket);
         if(order==NULL) continue;
         //--- Add a pending order object to the list of market orders and positions
         if(!this.AddToListMarket(order))
            continue;
         //--- If there is no order in the list of control orders and positions, add it
         if(index==WRONG_VALUE)
           {
            if(!this.AddToListControl(order))
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Не удалось добавить контрольный ордер ","Failed to add control order "),order.TypeDescription()," #",order.Ticket());
              }
           }
         //--- If the order is already present in the list of control orders, check it for changed properties
         if(index>WRONG_VALUE)
           {
            this.OnChangeEvent(order,index);
           }
        }
     }
//--- MQ5
#else 
//--- Positionen
   int total_positions=::PositionsTotal();
   for(int i=0; i<total_positions; i++)
     {
      ulong ticket=::PositionGetTicket(i);
      if(ticket==0) continue;
      CMarketPosition *position=new CMarketPosition(ticket);
      if(position==NULL) continue;
      //--- Add a position object to the list of market orders and positions
      if(!this.AddToListMarket(position))
         continue;
      //--- Get the control order index by a position ticket and ID
      int index=this.IndexControlOrder(ticket,position.PositionID());
      //--- If the order is not present in the list of control orders, add it
      if(index==WRONG_VALUE)
        {
         if(!this.AddToListControl(position))
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Не удалось добавить контрольую позицию ","Failed to add control position "),position.TypeDescription()," #",position.Ticket());
           }
        }
      //--- If the order is already present in the list of control orders, check it for changed properties
      else if(index>WRONG_VALUE)
        {
         this.OnChangeEvent(position,index);
        }
     }
//--- Orders
   int total_orders=::OrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong ticket=::OrderGetTicket(i);
      if(ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderGetInteger(ORDER_TYPE);
      //--- Market order
      if(type<ORDER_TYPE_BUY_LIMIT)
        {
         CMarketOrder *order=new CMarketOrder(ticket);
         if(order==NULL) continue;
         //--- Add a market order object to the list of market orders and positions
         if(!this.AddToListMarket(order))
            continue;
        }
      //--- Pending order
      else
        {
         CMarketPending *order=new CMarketPending(ticket);
         if(order==NULL) continue;
         //--- Add a pending order object to the list of market orders and positions
         if(!this.AddToListMarket(order))
            continue;
         //--- Get the control order index by a position ticket and ID
         int index=this.IndexControlOrder(ticket,order.PositionID());
         //--- If the order is not present in the control order list, add it
         if(index==WRONG_VALUE)
           {
            if(!this.AddToListControl(order))
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Не удалось добавить контрольный ордер ","Failed to add control order "),order.TypeDescription()," #",order.Ticket());
              }
           }
         //--- If the order is already in the control order list, check it for changed properties
         else if(index>WRONG_VALUE)
           {
            this.OnChangeEvent(order,index);
           }
        }
     }
#endif 
//--- Erster Start
   if(this.m_struct_prev_market.hash_sum_acc==WRONG_VALUE)
     {
      this.SavePrevValues();
     }
//--- Falls die Hash-Summe aller Aufträge und Positionen sich geändert hat
   if(this.m_struct_curr_market.hash_sum_acc!=this.m_struct_prev_market.hash_sum_acc)
     {
      this.m_new_market_orders=this.m_struct_curr_market.total_market-this.m_struct_prev_market.total_market;
      this.m_new_pendings=this.m_struct_curr_market.total_pending-this.m_struct_prev_market.total_pending;
      this.m_new_positions=this.m_struct_curr_market.total_positions-this.m_struct_prev_market.total_positions;
      this.m_change_volume_value=::NormalizeDouble(this.m_struct_curr_market.total_volumes-this.m_struct_prev_market.total_volumes,4);
      this.m_is_change_volume=(this.m_change_volume_value!=0 ? true : false);
      this.m_is_trade_event=true;
      this.SavePrevValues();
     }
  }
//+------------------------------------------------------------------+

Das Verfahren zum Hinzufügen von Orders und Positionen zur Liste der Marktorders und Positionen der Kollektion:

//+--------------------------------------------------------------------------------+
//| Add an order or a position to the list of orders and positions on the account  |
//+--------------------------------------------------------------------------------+
bool CMarketCollection::AddToListMarket(COrder *order)
  {
   if(order==NULL)
      return false;
   ENUM_ORDER_STATUS status=order.Status();
   if(this.m_list_all_orders.InsertSort(order))
     {
      if(status==ORDER_STATUS_MARKET_POSITION)
        {
         this.m_struct_curr_market.hash_sum_acc+=order.GetProperty(ORDER_PROP_TIME_UPDATE_MSC)+this.ConvertToHS(order);
         this.m_struct_curr_market.total_volumes+=order.Volume();
         this.m_struct_curr_market.total_positions++;
         return true;
        }
      if(status==ORDER_STATUS_MARKET_PENDING)
        {
         this.m_struct_curr_market.hash_sum_acc+=this.ConvertToHS(order);
         this.m_struct_curr_market.total_volumes+=order.Volume();
         this.m_struct_curr_market.total_pending++;
         return true;
        }
     }
   else
     {
      ::Print(DFUN,order.TypeDescription()," #",order.Ticket()," ",TextByLanguage("не удалось добавить в список","failed to add to list"));
      delete order;
     }
   return false;
  }
//+------------------------------------------------------------------+

Der Zeiger auf die der Kollektionsliste hinzugefügte Order wird an die Methode übergeben. Nach Aufnahme einer Order in die Kollektionsliste werden die Daten der Struktur, die den aktuellen Stand der Marktorders und -positionen für eine spätere Überprüfung speichern und die Änderungen in der Anzahl der Orders und Positionen definieren, geändert abhängig vom Auftragsstatus.

  • Wenn dies eine Position ist, werden eine Positionsänderungszeit und ein berechneter Wert für die Hash-Summe zur allgemeinen Hash-Summe addiert, und die Gesamtpositionsnummer wird erhöht.
  • Wenn es sich um eine Pending-Order handelt, wird ein berechneter Wert für die Hash-Summe zur allgemeinen Hash-Summe addiert, und die Gesamtzahl der Pending Orders wird um erhöht.

Das Verfahren zum Erstellen eines Kontrollauftrags und Hinzufügen desselben zur Liste der Kontrollaufträge:

//+------------------------------------------------------------------+
//| Create and add an order to the list of control orders            |
//+------------------------------------------------------------------+
bool CMarketCollection::AddToListControl(COrder *order)
  {
   if(order==NULL) 
      return false;
   COrderControl* order_control=new COrderControl(order.PositionID(),order.Ticket(),order.Magic(),order.Symbol());
   if(order_control==NULL)
      return false;
   order_control.SetTime(order.TimeOpenMSC());         
   order_control.SetTimePrev(order.TimeOpenMSC());     
   order_control.SetVolume(order.Volume());            
   order_control.SetTime(order.TimeOpenMSC());         
   order_control.SetTypeOrder(order.TypeOrder());      
   order_control.SetTypeOrderPrev(order.TypeOrder());  
   order_control.SetPrice(order.PriceOpen());          
   order_control.SetPricePrev(order.PriceOpen());      
   order_control.SetStopLoss(order.StopLoss());        
   order_control.SetStopLossPrev(order.StopLoss());    
   order_control.SetTakeProfit(order.TakeProfit());    
   order_control.SetTakeProfitPrev(order.TakeProfit());
   if(!this.m_list_control.Add(order_control))
     {
      delete order_control;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Der Zeiger auf eine Marktorder und -position wird der Methode übergeben. Wenn ein ungültiges Objekt übergeben wird, wird false zurückgegeben.
Es wird dann eine neue Kontrollorder erstellt, so dass der Konstruktor sofort eine Positions-ID, ein Ticket, eine Magicnummer und ein Symbol eines an die Methode übergebenen Orderobjekts erhält. Alle Daten, die zur Identifizierung von Auftrags-/Positionsänderungen notwendig sind, werden dann ausgefüllt.
Wenn das Hinzufügen einer neuen Kontrollorder zur Liste der Kontrollorders fehlgeschlagen ist, wird der Auftrag entfernt und "false" wird zurückgegeben
.

Da wir immer wieder neue Orders und Positionen in die Liste der Kontrollaufträge und -positionen aufnehmen, kann es nach einer langen Arbeitszeit sehr umfangreich werden. Orders und Positionen leben nicht ewig, und ihre Kontrollkopien sollten nicht ohne Grund dauerhaft in der Liste gespeichert werden, die den Speicher belegt. Um unnötige Kontrollorders aus der Liste zu entfernen, verwenden wir die Methode DeleteOrderFromListControl(), um eine Kontrollorder durch ein Positionsticket und der ID aus der Liste der Kontrollorders zu entfernen.

Im Moment wird die Methode nur deklariert, aber nicht implementiert. Die Implementierung erfolgt nach Vorbereitung der gesamten Funktionalität zur Verfolgung von Auftrags- und Positionsänderungen.

Das Verfahren, das den Index der Kontrollorders in der Liste der Kontrollorders über ein Positionsticket und eine ID zurückgibt:

//+------------------------------------------------------------------+
//| Return an order index by a ticket in the list of control orders  |
//+------------------------------------------------------------------+
int CMarketCollection::IndexControlOrder(const ulong ticket,const ulong id)
  {
   int total=this.m_list_control.Total();
   for(int i=0;i<total;i++)
     {
      COrderControl* order=this.m_list_control.At(i);
      if(order==NULL)
         continue;
      if(order.PositionID()==id && order.Ticket()==ticket)
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Das Verfahren erhält Order-/Positionsticket und Positions-ID. Eine Kontrollorder mit einem passenden Ticket und ID wird aus allen Kontrollorders in der Schleife gesucht und deren Index wird in der Liste der Kontrollorder zurückgegeben. Wenn die Bestellung nicht gefunden wird, wird -1 zurückgegeben.

Ereignisbehandlungsmethode zum Ändern einer bestehenden Order/Position:

//+------------------------------------------------------------------+
//| Handler of changing an existing order/position                   |
//+------------------------------------------------------------------+
void CMarketCollection::OnChangeEvent(COrder* order,const int index)
  {
   COrderControl* order_control=this.m_list_control.At(index);
   if(order_control!=NULL)
     {
      this.m_change_type=order_control.ChangeControl(order);
      ENUM_CHANGE_TYPE change_type=(order.Status()==ORDER_STATUS_MARKET_POSITION ? CHANGE_TYPE_ORDER_TAKE_PROFIT : CHANGE_TYPE_NO_CHANGE);
      if(this.m_change_type>change_type)
        {
         order_control.SetNewState(order);
         if(!this.AddToListChanges(order_control))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить модифицированный ордер в список изменённых ордеров","Could not add modified order to list of modified orders"));
           }
        }
     }
  }
//+------------------------------------------------------------------+

Das Verfahren empfängt den Zeiger auf die geprüfte Order und den Index der entsprechenden Kontrollorder in der Liste der Kontrollorder.
Es ruft die Kontrollorder aus der Liste über ihren Index ab und überprüft die Änderungen in den Eigenschaften der Kontrollorder, die den Eigenschaften der mit der Methode ChangeControl() überprüften Kontrollorder entsprechen. Das Verfahren empfängt den Zeiger auf die Kontrollorder. Wenn eine Differenz gefunden wird, gibt die Methode den Änderungstyp zurück, der in die Klassenvariable m_change_type geschrieben wird.
Als Nächstes überprüfen wir den Status der geprüften Order und setzen den Wert, ab dem die Änderung als aufgetreten gilt. Für eine Position sollte dieser Wert die Konstante CHANGE_TYPE_ORDER_TAKE_PROFIT aus der Enumeration ENUM_CHANGE_TYPE überschreiten, da alle Werte, die gleich oder niedriger als diese Konstante sind, sich nur auf einer Pending-Order beziehen. Bei einer Pending-Order sollte der Wert die Konstante CHANGE_TYPE_NO_NO_CHANGE überschreiten.
Wenn die erhaltene Variable m_change_type die angegebene Variable überschreitet, wird eine Änderung erkannt. In diesem Fall wird der aktuelle Status der Kontrollorder für die spätere Prüfung gespeichert und eine Kopie einer Kontrollorder wird in die Liste der geänderten Orders für die spätere Bearbeitung der Liste in der Klasse CEventsCollection gestellt.

Das Verfahren zum Erstellen einer geänderten Kontrollorder und Hinzufügen desselben zur Liste der geänderten Order:

//+------------------------------------------------------------------+
//|Create and add a control order to the list of changed orders      |
//+------------------------------------------------------------------+
bool CMarketCollection::AddToListChanges(COrderControl* order_control)
  {
   if(order_control==NULL)
      return false;
   COrderControl* order_changed=new COrderControl(order_control.PositionID(),order_control.Ticket(),order_control.Magic(),order_control.Symbol());
   if(order_changed==NULL)
      return false;
   order_changed.SetTime(order_control.Time());                    
   order_changed.SetTimePrev(order_control.TimePrev());            
   order_changed.SetVolume(order_control.Volume());                
   order_changed.SetTypeOrder(order_control.TypeOrder());          
   order_changed.SetTypeOrderPrev(order_control.TypeOrderPrev());  
   order_changed.SetPrice(order_control.Price());                  
   order_changed.SetPricePrev(order_control.PricePrev());          
   order_changed.SetStopLoss(order_control.StopLoss());            
   order_changed.SetStopLossPrev(order_control.StopLossPrev());    
   order_changed.SetTakeProfit(order_control.TakeProfit());        
   order_changed.SetTakeProfitPrev(order_control.TakeProfitPrev());
   order_changed.SetChangedType(order_control.GetChangeType());    
   if(!this.m_list_changed.Add(order_changed))
     {
      delete order_changed;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode erhält den Zeiger auf die geänderte Kontrollorder. Die Kopie der Order sollte in die Liste der geänderten Kontrollorders und -positionen aufgenommen werden.
Anschließend wird eine neue Kontrollorder erstellt. Sie erhält sofort die Positions-ID, das Ticket, die Magicnummer und das Symbol, die mit denen der geänderten Kontrollorder übereinstimmen.
Danach werden die Eigenschaften der geänderten Kontrollorder einfach elementweise in die Eigenschaften des neu erstellten Elements kopiert.
Schließlich platzieren wir die neu angelegte Kopie einer geänderten Kontrollorder in der Liste der geänderten Orders.
Wenn die neu erstellte Order nicht in die Liste aufgenommen werden konnte, wird das neu erstellte Orderobjekt entfernt und false zurückgegeben.

Wir haben die Implementierung von Änderungen an der Klasse CMarketCollection abgeschlossen. Kommen wir nun zur Klasse CEventsCollection.

Die Kollektion der Ereignisklassen CEventsCollection sollte Behandlungsereignisse enthalten, bei denen die Liste der Kollektionen der Marktorders und Positionen angelegten geänderten Orders nicht leer ist. Das bedeutet, dass sie geänderte Order und Positionen enthält, die behandelt werden sollen (erstellen eines neuen Ereignisses und Senden der entsprechenden Nachricht an das aufrufende Programm).

Fügen wir die Definition der beiden Methoden zusätzlich zur bereits vorhandenen Methode dem privaten Abschnitt der Klasse hinzu: die neue überladene Methode zum Erzeugen eines neuen Ereignisses und die Methode zum Behandeln der Änderung einer bestehenden Order/Position, während die Methode Refresh() die Fähigkeit erhält, die Liste der geänderten Order der Methode zu übergeben:

//+------------------------------------------------------------------+
//|                                             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
   int               m_trade_event_code;              // Trading event code
   ENUM_TRADE_EVENT  m_trade_event;                   // Handelsereignis auf dem Konto
   CEvent            m_event_instance;                // Ereignisobjekt für die Suche nach einer Eigenschaft
   
//--- Create a trading event depending on the (1) order status and (2) change type
   void              CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market);
   void              CreateNewEvent(COrderControl* order);
//--- Create an event for a (1) hedging account, (2) netting account
   void              NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
   void              NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
//--- Auswahl und Rückgabe der Liste der Pending-Marktorder
   CArrayObj*        GetListMarketPendings(CArrayObj* list);
//--- Select from the list and return the list of historical (1) removed pending orders, (2) deals, (3) all closing orders 
   CArrayObj*        GetListHistoryPendings(CArrayObj* list);
   CArrayObj*        GetListDeals(CArrayObj* list);
   CArrayObj*        GetListCloseByOrders(CArrayObj* list);
//--- Return the list of (1) all position orders by its ID, (2) all deal positions by its ID,
//--- (3) all market entry deals by position ID, (4) all market exit deals by position ID,
//--- (5) all position reversal deals by position 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);
   CArrayObj*        GetListAllDealsInOutByPosID(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);
//--- Return the (1) first, (2) last and (3) closing order from the list of all position orders,
//--- (4) an order by ticket, (5) market position by ID,
//--- (6) the last and (7) penultimate InOut deal by position ID
   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*           GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket);
   COrder*           GetPositionByID(CArrayObj* list,const ulong position_id);
//--- Rückgabe des Flags des Ereignisobjekts in der Ereignisliste
   bool              IsPresentEventInList(CEvent* compared_event);
//--- Handler of an existing order/position change
   void              OnChangeEvent(CArrayObj* list_changes,CArrayObj* list_history,CArrayObj* list_market,const int index);
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,
                             CArrayObj* list_changes,
                             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);
  };
//+------------------------------------------------------------------+

Lassen Sie uns die neuen Methoden außerhalb des Klassenkörpers implementieren.

Die überladene Methode zum Erzeugen eines Änderungsereignisses von Order/Position:

//+------------------------------------------------------------------+
//| Create a trading event depending on the order change type        |
//+------------------------------------------------------------------+
void CEventsCollection::CreateNewEvent(COrderControl* order)
  {
   CEvent* event=NULL;
//--- Pending StopLimit order placed
   if(order.GetChangeType()==CHANGE_TYPE_ORDER_TYPE)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED;
      event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket());
     }
//--- 
   if(event!=NULL)
     {
      event.SetProperty(EVENT_PROP_TIME_EVENT,order.Time());                        // Event time
      event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_STOPLIMIT_TRIGGERED);  // Event reason (from the ENUM_EVENT_REASON enumeration)
      event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrderPrev());          // Type of the order that triggered an event
      event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // Ticket of the order that triggered an event
      event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());             // Event order type
      event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());              // Event order ticket
      event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());          // Position first order type
      event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());           // Position first order ticket
      event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // Positions-ID
      event.SetProperty(EVENT_PROP_POSITION_BY_ID,0);                               // Opposite position ID
      event.SetProperty(EVENT_PROP_MAGIC_BY_ID,0);                                  // Opposite position magic number
         
      event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrderPrev());      // Position order type before changing the direction
      event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());           // Position order ticket before changing direction
      event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());         // Current position order type
      event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());          // Current position order ticket
         
      event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // Order magic number
      event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimePrev());           // First position order time
      event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PricePrev());                  // Event price
      event.SetProperty(EVENT_PROP_PRICE_OPEN,order.Price());                       // Order open price
      event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.Price());                      // Order close price
      event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                      // StopLoss order price
      event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                    // TakeProfit order price
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());            // Requested order volume
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,0);                        // Executed order volume
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.Volume());            // Remaining (unexecuted) order volume
      event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0);                     // Executed position volume
      event.SetProperty(EVENT_PROP_PROFIT,0);                                       // Profit
      event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // Symbol des Auftrags
      event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                    // Opposite position symbol
      //--- 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;
        }
     }
  }
//+------------------------------------------------------------------+

Ich habe die neue Methode zur Ereigniserzeugung im fünften Teil der Bibliotheksbeschreibung beim Erstellen einer Ereigniskollektion beschrieben.
Diese Methode ist fast identisch. Der einzige Unterschied ist der Typ der Order, der Zeiger, der der Methode übergeben wird.
Die Art einer aufgetretenen Orderänderung wird gleich zu Beginn der Methode geprüft und der Änderungscode wird in der Klassenvariablen m_trade_event_code entsprechend der Änderungsart gesetzt.
Anschließend wird das dem Änderungstyp entsprechende Ereignis erstellt, seine Eigenschaften werden entsprechend dem Änderungstyp gefüllt, das Ereignis wird in die Ereignisliste und an das Kontrollprogramm gesendet.

Die verbesserte Methode zur Aktualisierung der Ereignisliste:

//+------------------------------------------------------------------+
//| Aktualisieren der Ereignisliste                                  |
//+------------------------------------------------------------------+
void CEventsCollection::Refresh(CArrayObj* list_history,
                                CArrayObj* list_market,
                                CArrayObj* list_changes,
                                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;
//--- Wenn das Ereignis in der Umgebung des Marktes existiert
   if(is_market_event)                         
     {                                         
      //--- if the order properties were changed 
      int total_changes=list_changes.Total();  
      if(total_changes>0)                      
        {                                      
         for(int i=total_changes-1;i>=0;i--)   
           {                                   
            this.OnChangeEvent(list_changes,i);
           }                                   
        }                                      
      //--- 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. If this is a removed pending order without a position ID, 
               //--- this is an order removal - set a trading event
               COrder* order=list.At(i);
               if(order!=NULL && order.Status()==ORDER_STATUS_HISTORY_PENDING && order.PositionID()==0)
                  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);
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

 Diese Methode wurde auch im fünften Teil der Bibliotheksbeschreibung beim Erstellen einer Ereigniskollektion berücksichtigt. Der Unterschied zu dieser Methode liegt im hinzugefügten Codeblock zur Behandlung von Änderungsereignissen, falls die Größe der geänderten Orderliste nicht Null ist. Jede geänderte Reihenfolge aus der Liste wird in der Schleife der Methode für die Ereignisbehandlung der geänderten Order behandelt:

//+------------------------------------------------------------------+
//| The handler of an existing order/position change event           |
//+------------------------------------------------------------------+
void CEventsCollection::OnChangeEvent(CArrayObj* list_changes,const int index)
  {
   COrderControl* order_changed=list_changes.Detach(index);
   if(order_changed!=NULL)
     {
      if(order_changed.GetChangeType()==CHANGE_TYPE_ORDER_TYPE)
        {
         this.CreateNewEvent(order_changed);
        }
      delete order_changed;
     }
  }
//+------------------------------------------------------------------+

Wenn wir die Liste der geänderten Orders bearbeiten, müssen wir eine geänderte Order aus der Liste erhalten und das Orderobjekt und den entsprechenden Zeiger aus der Liste entfernen, nachdem die Bearbeitung abgeschlossen ist, um zu vermeiden, dass das gleiche Ereignis mehrmals behandelt wird.
Glücklicherweise stellt die Standardbibliothek bei der Arbeit mit dem dynamischen Array von Objektzeigern CArrayObj die Methode Detach() zur Verfügung, die das Element von der angegebenen Position empfängt und aus dem Array entfernt. Mit anderen Worten, wir erhalten den Zeiger auf das Objekt , das im Array mit dem Index gespeichert ist, und entfernen diesen Zeiger aus dem Array. Wenn die Änderungsart CHANGE_TYPE_ORDER_TYPE ist (Änderung des Ordertyps — löst eine Pending-StopLimit-Order aus und verwandelt sie in eine Limit-Order), erzeugt ein neues Ereignis — Aktivierung einer StopLimit-Order. Nachdem das Objekt von dem mit der Methode Detach() erhaltenen Zeiger behandelt wird, wird der Zeiger (der nicht mehr benötigt wird) einfach entfernt.

Damit ist die Verbesserung der Klasse CEventsCollection abgeschlossen.

Damit alle Änderungen wirksam werden, sollte die Liste der geänderten Orders aus der Kollektionklasse der Marktorders und Positionen empfangen und ihre Größe in das Hauptobjekt der Bibliothek geschrieben werden — in die Klasse CEngine (die Methode TradeEventsControl()). Wenn die Aktualisierungsmethode Refresh() der Klasse der Ereigniskollektion aufgerufen wird, sollte zusätzlich die Größe der Liste der geänderten Order überprüft werden, während die Liste der geänderten Orders an die Methode Refresh() der Ereigniskollektion zur Bearbeitung übergeben werden sollte:

//+------------------------------------------------------------------+
//| Prüfen der Handelsereignisse                                     |
//+------------------------------------------------------------------+
void CEngine::TradeEventsControl(void)
  {
//--- Initialisierung des Codes des Handelsereignisses un des Flags
   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
   int change_total=0;
   CArrayObj* list_changes=this.m_market.GetListChanges();
   if(list_changes!=NULL)
      change_total=list_changes.Total();
   if(this.m_is_history_trade_event || this.m_is_market_trade_event || change_total>0)
     {
      this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(),list_changes,
                            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();
     }
  }
//+------------------------------------------------------------------+

Da die Aktivierung einer StopLimit-Order zur Platzierung einer Limit-Order führt, werden wir dieses Event als Pending-Order "qualifizieren", während die Ereignisursache die Aktivierung einer StopLimit-Order EVENT_REASON_STOPLIMIT_TRIGGERED ist. Wir haben diese Konstante bereits in der Enumeration ENUM_EVENT_REASON der Datei Defines.mqh definiert.

Lassen Sie uns die Klasse EventOrderPlased verbessern, um das Ereignisprogramm im Journal anzuzeigen und an das Kontrollprogramm zu senden:

Fügen wir einfach die Ereignisursache EVENT_REASON_STOPLIMIT_TRIGGERED 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 Order-Eigenschaften (1) real, (2) integer
   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 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;
  }
//+------------------------------------------------------------------+
//| Anzeige einer kurzen Nachricht über das Ereignis im Journal      |
//+------------------------------------------------------------------+
void CEventOrderPlased::PrintShort(void)
  {
   int    digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),digits) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),digits) : "");
   string vol=::DoubleToString(this.VolumeOrderInitial(),DigitsLots(this.Symbol()));
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string type=this.TypeOrderFirstDescription()+" #"+(string)this.TicketOrderEvent();
   string event=TextByLanguage(" Установлен "," Placed ");
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),digits);
   string txt=head+this.Symbol()+event+vol+" "+type+price+sl+tp+magic;
   //--- If StopLimit order is activated
   if(this.Reason()==EVENT_REASON_STOPLIMIT_TRIGGERED)
     {
      head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimeEvent())+" -\n";
      event=TextByLanguage(" Сработал "," Triggered ");
      type=
        (
         OrderTypeDescription(this.TypeOrderPosPrevious())+" #"+(string)this.TicketOrderEvent()+
         TextByLanguage(" по цене "," at price ")+DoubleToString(this.PriceEvent(),digits)+" -->\n"+
         vol+" "+OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderEvent()+
         TextByLanguage(" на цену "," on price ")+DoubleToString(this.PriceOpen(),digits)
        );
      txt=head+this.Symbol()+event+"("+TimeMSCtoString(this.TimePosition())+") "+vol+" "+type+sl+tp+magic;
     }
   ::Print(txt);
  }
//+------------------------------------------------------------------+
//| 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());
  }
//+------------------------------------------------------------------+

Hier ist alles ganz einfach zu verstehen. Es hat keinen Sinn, sich mit einfachen Verfahren zu beschäftigen.

Damit ist die Verbesserung der Bibliothek zur Verfolgung einer StopLimit-Order abgeschlossen.

Test

Um die implementierten Verbesserungen zu testen, werden wir den EA aus dem vorherigen Artikel verwenden. Benennen Sie einfach den TestDoEasyPart06.mq5 EA aus dem Ordner \MQL5\Experts\TestDoEasy\Part06 in TestDoEasyPart07.mq5 um und speichern Sie ihn im neuen Unterordner \MQL5\Experts\TestDoEasy\ Part07.

Kompilieren Sie den EA, starten Sie ihn im Tester, platzieren Sie eine StopLimit-Order und warten Sie auf die Aktivierung:



Was kommt als Nächstes?

Die im Artikel implementierte Funktionsweise beinhaltet die Möglichkeit, die Verfolgung anderer Ereignisse schnell hinzuzufügen: Änderung der Eigenschaften einer Pending-Order — Preis, StopLoss und TakeProfit sowie Änderung der StopLoss und TakeProfit der Positionen. Wir werden diese Aufgaben im nächsten Artikel berücksichtigen.

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.
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm.
Teil 6. Ereignisse auf Netting-Konten.


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/6482

Beigefügte Dateien |
MQL5.zip (86.94 KB)
Beurteilung der Fähigkeit des Fraktalindex und des Hurst-Exponenten, finanzielle Zeitreihen vorherzusagen. Beurteilung der Fähigkeit des Fraktalindex und des Hurst-Exponenten, finanzielle Zeitreihen vorherzusagen.
Studien, die sich auf die Suche nach dem fraktalen Verhalten von Finanzdaten beziehen, deuten darauf hin, dass hinter dem scheinbar chaotischen Verhalten von ökonomischen Zeitreihen verborgene stabile Mechanismen des kollektiven Verhaltens der Teilnehmer stehen. Diese Mechanismen können zur Entstehung einer Preisdynamik an der Börse führen, die spezifische Eigenschaften von Preisreihen definieren und beschreiben kann. Im Handel könnte man von den Indikatoren profitieren, die effizient und zuverlässig die in der Praxis relevanten fraktalen Parameter in Umfang und Zeitrahmen schätzen können.
Anwendung von OLAP im Handel (Teil 2): Die Visualisierung der Ergebnisse der interaktiven, mehrdimensionalen Datenanalyse Anwendung von OLAP im Handel (Teil 2): Die Visualisierung der Ergebnisse der interaktiven, mehrdimensionalen Datenanalyse
In diesem Artikel betrachten wir das Erstellen einer interaktiven grafischen Oberfläche für ein MQL-Programm, das für die Verarbeitung von Kontobewegungen und Handelsberichten mit OLAP-Techniken konzipiert ist. Für die Darstellung werden wir maximierbare und skalierbare Fenster, ein adaptives Layout der Gummikontrollen und ein neues Steuerelement für die Anzeige von Diagrammen verwenden. Damit die Darstellung funktioniert, implementieren wir eine GUI mit der Auswahl von Variablen entlang der Koordinatenachsen sowie mit der Auswahl von Aggregatfunktionen, Diagrammtypen und Sortieroptionen.
Messmethoden zur Preisgeschwindigkeit Messmethoden zur Preisgeschwindigkeit
Es gibt mehrere, unterschiedliche Ansätze zur Marktanalyse und -forschung. Die wichtigsten sind technische und fundamentale Analysen. In der technischen Analyse sammeln, verarbeiten und analysieren Händler numerische Daten und marktrelevante Parameter, einschließlich Preise, Volumina usw. In der Fundamentalanalyse analysieren Händler Ereignisse und Nachrichten, die die Märkte direkt oder indirekt betreffen. Der Artikel beschäftigt sich mit Messmethoden zur Preisgeschwindigkeit und untersucht darauf aufbauend Handelsstrategien.
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil VI): Ereignisse von Netting-Konten Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil VI): Ereignisse von Netting-Konten
In den vorherigen 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 fünften Teil der Artikelreihe haben wir Handelsereignisklassen und die Kollektion der Ereignisse angelegt, aus der die Ereignisse an das Basisobjekt der Motorenbibliothek und die Regelprogrammkarte gesendet werden. In diesem Teil werden wir die Bibliothek für die Arbeit an Netting-Konten weiterentwickeln.