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
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.
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 Ä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.
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
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.