Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil V). Klassen und Kollektionen für Handelsereignisse, Nachrichten an das Programm senden
Inhalt
- Neuordnung der Bibliotheksstruktur
- Ereignisklassen
- Kollektion von Handelsereignissen
- Testen der Prozesse zum Definieren, Behandeln und Empfangen von Ereignissen
- Was kommt als Nächstes?
Neuordnung der Bibliotheksstruktur
In den vorangegangenen Artikeln haben wir begonnen, eine große plattformübergreifende Bibliothek zu erstellen, die die Entwicklung von Programmen für MetaTrader 5 und MetaTrader 4 Plattformen vereinfacht. Im vierten Teil haben wir die Verfolgung von Handelsereignissen auf dem Konto getestet. In diesem Artikel werden wir Klassen für Handelsereignisse entwickeln und diese in der Kollektion der Ereignisse platzieren. Von dort aus werden sie an das Basisobjekt der Enginebibliothek und die Steuerelement des Chartprogramms.
Aber zuerst wollen wir den Boden für die Weiterentwicklung der Bibliotheksstruktur bereiten.
Da wir viele verschiedene Kollektionen haben werden, und jede Kollektion ihre eigenen Objekte hat, die nur dieser Kollektion eigen sind, erscheint es sinnvoll, Objekte für jede Kollektion in separaten Unterordnern zu speichern.
Um dies zu tun, erstellen wir die Verzeichnisse Orders und Events im Unterordner Objects des
Bibliotheksstamm-Verzeichnisses
DoEasy.
Verschieben Sie alle zuvor erstellten Klassen aus dem Ordner Objects in den Ordner Orders, während der Ordner Events
die Klassen der Ereignisobjekte speichert, die wir in diesem Artikel entwickeln werden.
Verschieben Sie auch die Datei Select.mqh aus Collections in Services, da wir ihr noch eine weitere Serviceklasse hinzufügen werden.
Die Klasse verfügt über Methoden für den schnellen Zugriff auf beliebige Eigenschaften von Objekten aus den bestehenden und zukünftigen
Kollektionen, d.h. sie sollte sich im Ordner der Service-Klassen befinden.
Nach dem Verschieben der Datei der Klasse CSelect und dem Verschieben der Auftragsobjektklassen in das neue Verzeichnis ändern sich auch die relativen Adressen der für ihre Kompilierung benötigten Dateien. Deshalb gehen wir durch die Listen der verschobenen Klassen und ersetzen die Adressen der darin enthaltenen Dateien:
In der Datei Order.mqh ersetzen wir den Pfad für das Einbinden der Datei mit den der Servicefunktionen.
#include "..\Services\DELib.mqh"
mit
#include "..\..\Services\DELib.mqh"
In der Datei HistoryCollection.mqh, ersetzen wir die Pfade
#include "Select.mqh" #include "..\Objects\HistoryOrder.mqh" #include "..\Objects\HistoryPending.mqh" #include "..\Objects\HistoryDeal.mqh"
mit
#include "..\Services\Select.mqh" #include "..\Objects\Orders\HistoryOrder.mqh" #include "..\Objects\Orders\HistoryPending.mqh" #include "..\Objects\Orders\HistoryDeal.mqh"
In der Datei MarketCollection.mqh, ersetzen wir die Pfade
#include "Select.mqh" #include "..\Objects\MarketOrder.mqh" #include "..\Objects\MarketPending.mqh" #include "..\Objects\MarketPosition.mqh"
mit
#include "..\Services\Select.mqh" #include "..\Objects\Orders\MarketOrder.mqh" #include "..\Objects\Orders\MarketPending.mqh" #include "..\Objects\Orders\MarketPosition.mqh"
Jetzt sollte sich alles ohne Fehler kompilieren lassen.
Da die Anzahl der anstehenden Kollektionen enorm ist, wäre es gut, den Besitz der Liste der Kollektionen anhand von CArrayObj zu unterscheiden, um die Liste zu identifizieren. Jede Kollektion verfügt über eine Methode, die den Zeiger auf die vollständige Liste der Kollektionen zurückführt. Wenn es irgendwo eine Methode gibt, die eine bestimmte Liste einer bestimmten Kollektion empfängt, dann müssen wir innerhalb dieser Methode in der Lage sein, die Liste, die an die Methode übergeben wird, genau zu identifizieren, indem sie zu der einen oder anderen Kollektion gehört, um zu vermeiden, dass ein zusätzliches Flag übergeben wird, das den Typ der an die Methode übergebenen Liste anzeigt.
Glücklicherweise bietet die Standardbibliothek dafür bereits das notwendige Werkzeug in Form der virtuellen Methode Type(),
die die Objekt-ID zurückgibt.
Beispielsweise ist für CObject die
zurückgegebene ID 0, während für
CArrayObj die ID 0x7778 ist.
Da die Methode virtuell ist, können die Nachkommen der Klassen ihre eigenen Methoden verwenden, die bestimmte IDs zurückgeben.
Alle unsere Liste der Kollektionen basieren auf der Klasse CArrayObj. Wir erstellen unsere eigene Klasse CListObj , die ein Nachkomme der Klasse CArrayObj ist, und die Listen-ID wird in ihrer virtuellen Methode Type() zurückgegeben. Die ID selbst wird im Klassenkonstruktor als Konstante gesetzt. So werden wir weiterhin Zugriff auf unsere Kollektionen wie auf das Objekt CArrayObj erhalten, aber jetzt wird jede Liste ihre eigene spezifische ID haben.
Zuerst setzen wir die notwendigen IDs der Listen der Kollektionen in der Datei Definiert.mqh und fügen das Makro hinzu, das die Funktion mit der Fehlerzeilennummer beschreibt, um Debugging-Meldungen anzuzeigen, die eine Zeichenkette enthalten, von der diese Nachricht gesendet wird, um das Problem während des Debuggens im Code zu lokalisieren:
//+------------------------------------------------------------------+ //| Macro-Substitution | //+------------------------------------------------------------------+ //--- Beschreibung der Funktion mit der Zeilennummer des Fehlers #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Funktionsbeschreibung" #define COUNTRY_LANG ("Russian") // Landessprache #define END_TIME (D'31.12.3000 23:59:59') // Enddatum der abgefragten Kontohistorie #define TIMER_FREQUENCY (16) // Minimalfrequenz des Timers der Bibliothek in Millisekunden #define COLLECTION_PAUSE (250) // Pause des Timers der Kollektion der Aufträge und Deals in Millisekunden #define COLLECTION_COUNTER_STEP (16) // Increment des Timerzählers der Kollektion der Aufträge und Deals #define COLLECTION_COUNTER_ID (1) // Timerzählers der Kollektion von Aufträgen und Deals #define COLLECTION_HISTORY_ID (0x7778+1) // ID der Historische Kollektionsliste #define COLLECTION_MARKET_ID (0x7778+2) // ID der Marktkollektionsliste #define COLLECTION_EVENTS_ID (0x7778+3) // ID der Ereigniskollektionsliste //+------------------------------------------------------------------+
Erstellen wir nun die Klassse CListObj in der Datei ListObj.mqh im Ordner Collections. Die Basisklasse dafür ist CArrayObj:
//+------------------------------------------------------------------+ //| ListObj.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| Klasse der Kollektionsliste | //+------------------------------------------------------------------+ class CListObj : public CArrayObj { private: int m_type; // Listentyp public: void Type(const int type) { this.m_type=type; } virtual int Type(void) const { return(this.m_type); } CListObj() { this.m_type=0x7778; } }; //+------------------------------------------------------------------+
Alles, was wir hier tun müssen, ist, ein Mitglied der Klasse, die den
Listentyp enthält, zu deklarieren, die Methode zur Definition des
Listentyps und die Methode virtual zur Rückgabe hinzuzufügen.
Setzen wir im Klassenkonstruktor den Standardlistentyp gleich dem der Liste
CArrajObj. Er kann dann von einem aufrufenden Programm aus mit der Methode Type() neu definiert werden.
Nun müssen wir alle Kollektionslisten von der Klasse erben, um jeder Liste eine eigene Such-ID zuordnen zu können. Diese ID ermöglicht es uns, den Besitz der Liste in allen Methoden zu verfolgen, an die die Liste übergeben wird.
Öffnen Sie die Datei HistoryCollection.mqh, biden Sie die
Klasse CListObj und leiten Sie die Klasse CHistoryCollection von CListObj
ab.
//+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\HistoryOrder.mqh" #include "..\Objects\Orders\HistoryPending.mqh" #include "..\Objects\Orders\HistoryDeal.mqh" //+------------------------------------------------------------------+ //| Kollektion der historischen Aufträge und Deals | //+------------------------------------------------------------------+ class CHistoryCollection : public CListObj {
Im Klassenkonstruktor definieren den historischen Kollektionslistentyp, den wir als COLLECTION_HISTORY_ID in der Datei Defines.mqh definiert haben:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection(void) : m_index_deal(0),m_delta_deal(0),m_index_order(0),m_delta_order(0),m_is_trade_event(false) { this.m_list_all_orders.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN #else SORT_BY_ORDER_TIME_CLOSE #endif ); this.m_list_all_orders.Clear(); this.m_list_all_orders.Type(COLLECTION_HISTORY_ID); } //+------------------------------------------------------------------+
Wir machen das gleiche mit der Klasse CMarketCollection in der Datei MarketCollection.mqh:
//+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\MarketOrder.mqh" #include "..\Objects\Orders\MarketPending.mqh" #include "..\Objects\Orders\MarketPosition.mqh" //+------------------------------------------------------------------+ //| Kollektion der Marktorders und Positionen | //+------------------------------------------------------------------+ class CMarketCollection : public CListObj {
Definieren Sie im Klassenkonstruktor den Kollektionstyp für Market, die wir in der Datei Defines.mqh als COLLECTION_MARKET_ID angegeben haben:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CMarketCollection::CMarketCollection(void) : m_is_trade_event(false),m_is_change_volume(false),m_change_volume_value(0) { this.m_list_all_orders.Sort(SORT_BY_ORDER_TIME_OPEN); this.m_list_all_orders.Clear(); ::ZeroMemory(this.m_struct_prev_market); this.m_struct_prev_market.hash_sum_acc=WRONG_VALUE; this.m_list_all_orders.Type(COLLECTION_MARKET_ID); } //+------------------------------------------------------------------+
Nun hat jede der Kollektionslisten ihre ID, was die Identifizierung von Listen nach ihrem Typ vereinfacht.
Da wir neue Kollektionen für die Arbeit mit neuen Datentypen hinzufügen sollen (einschließlich der Kollektion von Kontoereignissen im vorliegenden Artikel), werden wir die neuen Enumeration verwenden. Um Namenskonflikte zu vermeiden, müssen wir die Namen einiger zuvor erstellter Makro-Substitutionen ersetzen:
//+------------------------------------------------------------------+ //| Mögliche Sortierkriterien von Aufträgen und Deals | //+------------------------------------------------------------------+ #define FIRST_ORD_DBL_PROP (ORDER_PROP_INTEGER_TOTAL) #define FIRST_ORD_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL) enum ENUM_SORT_ORDERS_MODE { //--- Sortieren nach den Integer-Eigenschaften SORT_BY_ORDER_TICKET = 0, // Sortieren nach Auftrags-Ticket SORT_BY_ORDER_MAGIC = 1, // Sortieren nach der Magicnummer SORT_BY_ORDER_TIME_OPEN = 2, // Sortieren nach Eröffnungszeit SORT_BY_ORDER_TIME_CLOSE = 3, // Sortieren nach Schlusszeit SORT_BY_ORDER_TIME_OPEN_MSC = 4, // Sortieren nach Eröffnungszeit des Auftrags in Millisekunden SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // Sortieren nach der Schlusszeit des Auftrags in Millisekunden SORT_BY_ORDER_TIME_EXP = 6, // Sortieren nach der Verfallszeit SORT_BY_ORDER_STATUS = 7, // Sortieren nach dem Auftragsstatus (Marktorder/Pending-Order/Deal/Saldo, Gutschriften) SORT_BY_ORDER_TYPE = 8, // Sortieren nach dem Auftragstyp SORT_BY_ORDER_REASON = 10, // Sortieren nach Auftrag/Deal/Position/Quelle SORT_BY_ORDER_STATE = 11, // Sortieren nach dem Auftragsstatus SORT_BY_ORDER_POSITION_ID = 12, // Sortieren nach der Positions-ID SORT_BY_ORDER_POSITION_BY_ID = 13, // Sortieren nach der ID der entgegengesetzten Position SORT_BY_ORDER_DEAL_ORDER = 14, // Sortieren nach dem Auftrag als Basis des Deals SORT_BY_ORDER_DEAL_ENTRY = 15, // Sortieren nach der Richtung der Deals – IN, OUT or IN/OUT SORT_BY_ORDER_TIME_UPDATE = 16, // Sortieren nach der Änderungszeit der Position in Sekunden SORT_BY_ORDER_TIME_UPDATE_MSC = 17, // Sortieren nach der Änderungszeit der Position in Millisekunden SORT_BY_ORDER_TICKET_FROM = 18, // Sortieren nach dem Tickets der Ober-Order SORT_BY_ORDER_TICKET_TO = 19, // Sortieren nach der Ticket der abgeleiteten Order SORT_BY_ORDER_PROFIT_PT = 20, // Sortieren nach dem Auftragsgewinn in Points SORT_BY_ORDER_CLOSE_BY_SL = 21, // Sortieren nach dem Flag für das Schließen durch StopLoss SORT_BY_ORDER_CLOSE_BY_TP = 22, // Sortieren nach dem Flag für das Schließen durch TakeProfit //--- Sortieren nach den Double-Eigenschaften SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, // Sortieren nach dem Eröffnungspreis SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+1, // Sortieren nach Schließpreis SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+2, // Sortieren nach StopLoss-Preis SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+3, // Sortieren nach TakeProfit-Preis SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+4, // Sortieren nach Gewinn SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+5, // Sortieren nach Kommission SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+6, // Sortieren nach Swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+7, // Sortieren nach Volumen SORT_BY_ORDER_VOLUME_CURRENT = FIRST_ORD_DBL_PROP+8, // Sortieren nach nicht ausgeführtem Volumen SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+9, // Sortieren nach Gewinn+Kommission+Swap SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+10, // Sortieren nach Limit-Order wenn StopLimit-Order aktiviert ist //--- Sortieren nach den String-Eigenschaften SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, // Sortieren nach dem Symbol SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+1, // Sortieren nach dem Kommentar SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+2 // Sortieren nach Auftrags-ID eines externen Handelssystems }; //+------------------------------------------------------------------+
Da wir derzeit die Dateien Defines.mqh bearbeiten, fügen Sie alle notwendigen Enumerationen der Ereignisklassen und die Kollektion der Kontoereignisse hinzu:
//+------------------------------------------------------------------+ //| Ereignisstatus | //+------------------------------------------------------------------+ enum ENUM_EVENT_STATUS { EVENT_STATUS_MARKET_POSITION, // Ereignis einer Marktposition (Eröffnen, teilweises Eröffnen, teilweises Schließen, Volumenerhöhung, Umkehrung) EVENT_STATUS_MARKET_PENDING, // Ereignis einer Pending-Order im Markt (Platzieren) EVENT_STATUS_HISTORY_PENDING, // Ereignis einer historische Pending-Order (Entfernen) EVENT_STATUS_HISTORY_POSITION, // Ereignis einer historischen Position (Schließen) EVENT_STATUS_BALANCE, // Ereignis einer Saldenoperation (Gutschrift, Abbuchung von Geldern und Ereignisse vom Typ aus der Enumeration ENUM_DEAL_TYPE) }; //+------------------------------------------------------------------+ //| Ereignisgrund | //+------------------------------------------------------------------+ enum ENUM_EVENT_REASON { EVENT_REASON_ACTIVATED_PENDING = 0, // Pending-Order Aktivierung EVENT_REASON_ACTIVATED_PENDING_PARTIALLY = 1, // Pending-Order teilweise Aktivierung EVENT_REASON_CANCEL = 2, // Storniert EVENT_REASON_EXPIRED = 3, // Verfallszeit des Auftrags EVENT_REASON_DONE = 4, // Anfrage voll ausgeführt EVENT_REASON_DONE_PARTIALLY = 5, // Anfrage teilweise ausgeführt EVENT_REASON_DONE_SL = 6, // Schließen durch StopLoss EVENT_REASON_DONE_SL_PARTIALLY = 7, // Teilweise geschlossen durch StopLoss EVENT_REASON_DONE_TP = 8, // Schließen durch TakeProfit EVENT_REASON_DONE_TP_PARTIALLY = 9, // Teilweise geschlossen durch TakeProfit EVENT_REASON_DONE_BY_POS = 10, // Geschlossen durch entgegengesetzte Position EVENT_REASON_DONE_PARTIALLY_BY_POS = 11, // Teilweise geschlossen durch eine entgegengesetzte Position EVENT_REASON_DONE_BY_POS_PARTIALLY = 12, // Geschlossen durch eine Gegenposition mit Teilvolumen EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY = 13, // Teilweises Schließen durch eine Gegenposition mit Teilvolumen //--- Konstanten bezüglich des Dealtyps DEAL_TYPE_BALANCE aus der Enumeration ENUM_DEAL_TYPE EVENT_REASON_BALANCE_REFILL = 14, // Nachfüllen des Saldos EVENT_REASON_BALANCE_WITHDRAWAL = 15, // Gelder vom Konto abziehen //--- Liste der Konstanten bezüglich der Enumeration TRADE_EVENT_ACCOUNT_CREDIT aus der Enumeration ENUM_TRADE_EVENT und verschoben auf +13 relativ zu ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3) EVENT_REASON_ACCOUNT_CREDIT = 16, // Kontogutschrift EVENT_REASON_ACCOUNT_CHARGE = 17, // Zusätzliche Gebühren EVENT_REASON_ACCOUNT_CORRECTION = 18, // Korrekturbuchung EVENT_REASON_ACCOUNT_BONUS = 19, // Bonusgutschrift EVENT_REASON_ACCOUNT_COMISSION = 20, // Zusätzliche Kommissionen EVENT_REASON_ACCOUNT_COMISSION_DAILY = 21, // Kommission zum Ende des Handelstages EVENT_REASON_ACCOUNT_COMISSION_MONTHLY = 22, // Kommission zum Ende des Monats EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY = 23, // Agenten-Kommission zum Ende des Handelstages EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY = 24, // Agenten-Kommission zum Ende des Monats EVENT_REASON_ACCOUNT_INTEREST = 25, // Zinsgutschrift der freien Gelder EVENT_REASON_BUY_CANCELLED = 26, // Storniert durch einen Kauf-Deal EVENT_REASON_SELL_CANCELLED = 27, // Storniert durch einen Verkaufs-Deal EVENT_REASON_DIVIDENT = 28, // Dividendengutsschrift EVENT_REASON_DIVIDENT_FRANKED = 29, // Gutschrift steuerbefreiter Dividenden EVENT_REASON_TAX = 30 // Steuern }; #define REASON_EVENT_SHIFT (EVENT_REASON_ACCOUNT_CREDIT-3) //+------------------------------------------------------------------+ //| Integer-Eigenschaften von Ereignissen | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_INTEGER { EVENT_PROP_TYPE_EVENT = 0, // Handelsereignistyp des Kontos (von der Enumeration ENUM_TRADE_EVENT) EVENT_PROP_TIME_EVENT, // Zeitereignis in Millisekunden EVENT_PROP_STATUS_EVENT, // Ereignisstatur (von der Enumeration ENUM_EVENT_STATUS) EVENT_PROP_REASON_EVENT, // Ereignisgrund (von der Enumeration ENUM_EVENT_REASON) EVENT_PROP_TYPE_DEAL_EVENT, // Ereignis-Typ des Deals EVENT_PROP_TICKET_DEAL_EVENT, // Ereignis-Ticket des Deals EVENT_PROP_TYPE_ORDER_EVENT, // Auftragstyp auf Basis eines Auftrags des dadurch geöffneten Deal-Ereignisses (der letzte Auftrag zur Position) EVENT_PROP_TICKET_ORDER_EVENT, // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) EVENT_PROP_TIME_ORDER_POSITION, // Zeit eines Auftrags auf Basis des dadurch eröffneten Positions-Deals (der erste Positionsauftrag) EVENT_PROP_TYPE_ORDER_POSITION, // Auftragstyp Type auf Basis der Eröffnung der Position (der erste Auftrag zur Position) EVENT_PROP_TICKET_ORDER_POSITION, // Ticket eines Auftrags auf Basis des dadurch eröffneten Positions-Deal (der erste Positionsauftrag) EVENT_PROP_POSITION_ID, // Positions-ID EVENT_PROP_POSITION_BY_ID, // ID der entgegengesetzten Position EVENT_PROP_MAGIC_ORDER, // Auftrag/Deal/Position Magicnummer }; #define EVENT_PROP_INTEGER_TOTAL (14) // Gesamtzahl der Integer-Eigenschaften der Ereignisse //+------------------------------------------------------------------+ //| Double-Eigenschaften von Ereignissen | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_DOUBLE { EVENT_PROP_PRICE_EVENT = (EVENT_PROP_INTEGER_TOTAL), // Preis, bei dem ein Ereignis auftritt EVENT_PROP_PRICE_OPEN, // Order/Deal/Position Eröffnungspreis EVENT_PROP_PRICE_CLOSE, // Order/Deal/Position Schließpreis EVENT_PROP_PRICE_SL, // StopLoss Order/Deal/Position EVENT_PROP_PRICE_TP, // TakeProfit Order/Deal/Position EVENT_PROP_VOLUME_INITIAL, // Verlangtes Volumen EVENT_PROP_VOLUME_EXECUTED, // Ausgeführtes Volumen EVENT_PROP_VOLUME_CURRENT, // Verbliebenes Volumen EVENT_PROP_PROFIT // Gewinn }; #define EVENT_PROP_DOUBLE_TOTAL (9) // Gesamtzahl der Double-Eigenschaften //+------------------------------------------------------------------+ //| String-Eigenschaften von Ereignissen | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_STRING { EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // Auftragssymbol }; #define EVENT_PROP_STRING_TOTAL (1) // Gesamtzahl der String-Eigenschaften //+------------------------------------------------------------------+ //| Mögliche Sortierkriterien von Ereignissen | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL) enum ENUM_SORT_EVENTS_MODE { //--- Sortieren nach den Integer-Eigenschaften SORT_BY_EVENT_TYPE_EVENT = 0, // Sortieren nach Ereignis-Typ SORT_BY_EVENT_TIME_EVENT = 1, // Sortieren nach Ereignis-Zeit SORT_BY_EVENT_STATUS_EVENT = 2, // Sortieren nach Ereignis-Status (aus der Enumeration ENUM_EVENT_STATUS) SORT_BY_EVENT_REASON_EVENT = 3, // Sortieren nach Ereignis-Grund (aus der Enumeration ENUM_EVENT_REASON) SORT_BY_EVENT_TYPE_DEAL_EVENT = 4, // Sortieren nach Ereignis-Typ des Deals SORT_BY_EVENT_TICKET_DEAL_EVENT = 5, // Sortieren nach Ticket des Deal-Ereignisses SORT_BY_EVENT_TYPE_ORDER_EVENT = 6, // Sortieren nach Auftragstyp auf Basis des Deal-Ereignisses (der letzte Auftrag zur Position) SORT_BY_EVENT_TYPE_ORDER_POSITION = 7, // Sortieren nach Auftragstyp auf Basis der Eröffnung der Position (der erste Auftrag zur Position) SORT_BY_EVENT_TICKET_ORDER_EVENT = 8, // Sortieren nach dem Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) SORT_BY_EVENT_TICKET_ORDER_POSITION = 9, // Sortieren nach dem Ticket eines Auftrags auf Basis des dadurch eröffneten Positions-Deal (der erste Positionsauftrag) SORT_BY_EVENT_POSITION_ID = 10, // Sortieren nach der Positions-ID SORT_BY_EVENT_POSITION_BY_ID = 11, // Sortieren nach der entgegengesetzten Positions-ID SORT_BY_EVENT_MAGIC_ORDER = 12, // Sortieren nach Auftrag/Deal/Position Magicnummer SORT_BY_EVENT_TIME_ORDER_POSITION = 13, // Sortieren nach der Zeit des Auftrages auf Basis des dadurch eröffneten Positions-Deals (der erste Positionsauftrag) //--- Sortieren nach den Double-Eigenschaften SORT_BY_EVENT_PRICE_EVENT = FIRST_EVN_DBL_PROP, // Sortieren nach einem Preis, bei dem ein Ereignis auftrat SORT_BY_EVENT_PRICE_OPEN = FIRST_EVN_DBL_PROP+1, // Sortieren nach Eröffnungspreis der Position SORT_BY_EVENT_PRICE_CLOSE = FIRST_EVN_DBL_PROP+2, // Sortieren nach Schließpreis der Position SORT_BY_EVENT_PRICE_SL = FIRST_EVN_DBL_PROP+3, // Sortieren nach dem StopLoss der Position SORT_BY_EVENT_PRICE_TP = FIRST_EVN_DBL_PROP+4, // Sortieren nach dem TakeProfit der Position SORT_BY_EVENT_VOLUME_INITIAL = FIRST_EVN_DBL_PROP+5, // Sortieren nach Anfangsvolumen SORT_BY_EVENT_VOLUME = FIRST_EVN_DBL_PROP+6, // Sortieren nach aktuellen Volumen SORT_BY_EVENT_VOLUME_CURRENT = FIRST_EVN_DBL_PROP+7, // Sortieren nach verbliebenen Volumen SORT_BY_EVENT_PROFIT = FIRST_EVN_DBL_PROP+8, // Sortieren nach Gewinn //--- Sortieren nach den String-Eigenschaften SORT_BY_EVENT_SYMBOL = FIRST_EVN_STR_PROP // Sortieren nach Auftrag/Deal/Position/Quelle Symbol }; //+------------------------------------------------------------------+
Hier haben wir alle möglichen Ereignisobjektzustände (ähnlich wie im ersten
Artikel beschrieben), Ereignisauftrittsgründe, alle Ereigniseigenschaften und Kriterien für die Sortierung von Ereignissen zur
Suche nach Eigenschaften. All dies ist bereits aus den vorangegangenen Artikeln bekannt. Wir können jederzeit
an den Anfang zurückkehren und die Daten zur Klärung aktualisieren.
Neben dem Ereignisstatus, der allgemeine Ereignisdaten liefert, enthält der Ereignisgrund (ENUM_EVENT_REASON) bereits alle Details zu
einer bestimmten Ereignisherkunft.
Wenn ein Ereignis beispielsweise den Status einer Marktposition (EVENT_STATUS_MARKET_POSITION) hat, wird der Grund für das
Auftreten des Ereignisses im Objektfeld EVENT_PROP_REASON_EVENT angegeben. Es kann entweder eine Pending Order Aktivierung
(EVENT_REASON_ACTIVATED_PENDING) oder das Eröffnen einer Position durch eine Market Order (EVENT_REASON_DONE) sein. Dabei werden
auch folgende Nuancen berücksichtigt: Wird eine Position teilweise eröffnet (nicht das gesamte Pending oder Marktordervolumen wurde
ausgeführt), ist der Ereignisgrund EVENT_REASON_ACTIVATED_PENDING_PARTIALLY oder EVENT_REASON_DONE_PARTIALLY etc.
Ein Ereignisobjekt enthält somit die gesamten Daten über das Ereignis und einen Auftrag, der es ausgelöst hat. Darüber hinaus liefern
historische Ereignisse Daten über zwei Aufträge — den ersten Positionsauftrag und den Auftrag die Position zu schließen.
So ermöglichen uns die Daten über Aufträge, Deals und die Positionen selbst im Ereignisobjekt die Verfolgung der gesamten Kette der
Positionsereignisse in der gesamten Geschichte ihrer Existenz — vom Öffnen bis zum Schließen.
Die Enumerationskonstanten ENUM_EVENT_REASON werden so angeordnet und nummeriert, dass der Ereignisstatus "Deal" der Deal-Typ auf die
Enumeration
ENUM_DEAL_TYPE fällt, falls der Dealtyp DEAL_TYPE_SELL überschreitet. So kommen wir schlussendlich zu den Typen
der Saldenoperationen. Die Beschreibung der Saldenoperation wird bei der Definition der Dealtyp in der zur Anlage vorbereiteten Klasse
an den Ereignisgrund gesendet.
Die Verschiebung, die dem Dealtyp hinzugefügt werden soll, wird in der Makroersetzung #define
REASON_EVENT_SHIFT gesetzt. Es ist notwendig, die Saldooperation in der Aufzählung ENUM_EVENT_REASON einzugliedern.
Fügen wir die Funktionen hinzu, die Beschreibungen von order, position und deal types zurückgeben, sowie die Funktion, die den Positionsnamen abhängig von der Art der Öffnung eines Auftrags zurückgibt. Alle Funktionen werden der Datei DELib.mqh in der Bibliothek Services hinzugefügt. Dies ermöglicht eine komfortable Ausgabe von Aufträgen, Positionen und Deals.
//+------------------------------------------------------------------+ //| Rückgabe des Auftragsnamen | //+------------------------------------------------------------------+ string OrderTypeDescription(const ENUM_ORDER_TYPE type) { string pref=(#ifdef __MQL5__ "Market order" #else "Position" #endif ); return ( type==ORDER_TYPE_BUY_LIMIT ? "Buy Limit" : type==ORDER_TYPE_BUY_STOP ? "Buy Stop" : type==ORDER_TYPE_SELL_LIMIT ? "Sell Limit" : type==ORDER_TYPE_SELL_STOP ? "Sell Stop" : #ifdef __MQL5__ type==ORDER_TYPE_BUY_STOP_LIMIT ? "Buy Stop Limit" : type==ORDER_TYPE_SELL_STOP_LIMIT ? "Sell Stop Limit" : type==ORDER_TYPE_CLOSE_BY ? TextByLanguage("Закрывающий ордер","Order for closing by") : #else type==ORDER_TYPE_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : type==ORDER_TYPE_CREDIT ? TextByLanguage("Кредитная операция","Credit operation") : #endif type==ORDER_TYPE_BUY ? pref+" Buy" : type==ORDER_TYPE_SELL ? pref+" Sell" : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Rückgabe des Positionsnamen | //+------------------------------------------------------------------+ string PositionTypeDescription(const ENUM_POSITION_TYPE type) { return ( type==POSITION_TYPE_BUY ? "Buy" : type==POSITION_TYPE_SELL ? "Sell" : TextByLanguage("Неизвестный тип позиции","Unknown position type") ); } //+------------------------------------------------------------------+ //| Rückgabe des Dealnamens | //+------------------------------------------------------------------+ string DealTypeDescription(const ENUM_DEAL_TYPE type) { return ( type==DEAL_TYPE_BUY ? TextByLanguage("Сделка на покупку","Buy deal") : type==DEAL_TYPE_SELL ? TextByLanguage("Сделка на продажу","Sell deal") : type==DEAL_TYPE_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : type==DEAL_TYPE_CREDIT ? TextByLanguage("Начисление кредита","Credit") : type==DEAL_TYPE_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : type==DEAL_TYPE_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : type==DEAL_TYPE_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : type==DEAL_TYPE_COMMISSION ? TextByLanguage("Дополнительные комиссии","Additional comissions") : type==DEAL_TYPE_COMMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : type==DEAL_TYPE_COMMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : type==DEAL_TYPE_COMMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : type==DEAL_TYPE_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Agency commission charged at the end of month") : type==DEAL_TYPE_BUY_CANCELED ? TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") : type==DEAL_TYPE_SELL_CANCELED ? TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") : type==DEAL_DIVIDEND ? TextByLanguage("Начисление дивиденда","Dividend operations") : type==DEAL_DIVIDEND_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : type==DEAL_TAX ? TextByLanguage("Начисление налога","Tax charges") : TextByLanguage("Неизвестный тип сделки","Unknown deal type") ); } //+------------------------------------------------------------------+ //| Rückgabe des Positionstyps nach dem Auftragstyp | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE PositionTypeByOrderType(ENUM_ORDER_TYPE type_order) { if( type_order==ORDER_TYPE_BUY || type_order==ORDER_TYPE_BUY_LIMIT || type_order==ORDER_TYPE_BUY_STOP #ifdef __MQL5__ || type_order==ORDER_TYPE_BUY_STOP_LIMIT #endif ) return POSITION_TYPE_BUY; else if( type_order==ORDER_TYPE_SELL || type_order==ORDER_TYPE_SELL_LIMIT || type_order==ORDER_TYPE_SELL_STOP #ifdef __MQL5__ || type_order==ORDER_TYPE_SELL_STOP_LIMIT #endif ) return POSITION_TYPE_SELL; return WRONG_VALUE; } //+------------------------------------------------------------------+
Beim Testen der Klasse der Ereigniskollektion wurde ein sehr unangenehmes Problem entdeckt: Beim Erstellen der Listen der Aufträge und Deals im Terminal mit HistorySelect() und dem anschließenden Zugriff auf die neuen Elemente der Listen stellte ich fest, dass die Aufträge nicht in der Reihenfolge ihres Auftretens, sondern nach ihrer Platzierungszeit gereiht werden. Lassen Sie es mich erklären:
- Eine Position eröffnen,
- Eine Pending-Order sofort platzieren
- Schließen eines Teils einer Position
- Warten, bis eine Pending-Order aktiviert wird
Es wird erwartet, dass die Reihenfolge der Ereignisse in der Historie wie folgt ist:
Öffnen einer Position,
Platzieren einer Pending-Order, Teilschließung, Aktivierung der Pending-Order — in der Reihenfolge der Durchführung von Operationen
im Ablauf der Zeit. Aber es stellte sich heraus, dass die Reihenfolge der Ereignisse in der gemeinsamen Reihenfolge und Dealhistorie wie
folgt ist:
- Öffnen einer Position
- Platzieren der Pending-Order
- Aktivieren der Pending-Order
- Teilschließung
Mit anderen Worten, die Geschichten der Aufträge und Deals leben ihr eigenes Leben innerhalb des Terminals und korrelieren nicht miteinander, was auch sinnvoll ist, da es sich um zwei Listen mit jeweils eigener Historie handelt.
Die Klasse der Kollektionen der Aufträge und Deals wird so gebildet, dass beim Ändern einer der Listen (Aufträge oder Deals) das letzte Ereignis auf dem Konto gelesen wird, um die Historie nicht ständig zu scannen, was sehr teuer wäre. Aber in Anbetracht des oben Gesagten und bei der Durchführung von Handelsoperationen verfolgen wir nicht die Reihenfolge der Aktionen. Wir geben einfach eine Bestellung auf und warten auf ihre Aktivierung. Nach der Eröffnung einer Position arbeiten wir ausschließlich mit ihr. In diesem Fall sind alle Ereignisse in der erforderlichen Reihenfolge zu sortieren, die eine Verfolgung ermöglicht. Dies ist jedoch nicht ausreichend. Wir müssen in jeder Reihenfolge arbeiten, und das Programm sollte in der Lage sein, das richtige Ereignis zu finden und genau darauf zu zeigen.
Basierend auf dem oben Gesagten habe ich die Klasse der Kollektionen der historischen Aufträge und Ereignisse verbessert. Wenn nun ein Ereignis außer der Reihe auftritt, findet die Klasse den notwendigen Auftrag, erzeugt ihr Objekt und stellt es in die Liste als letztes, so dass die Klasse der Ereigniskollektion immer in der Lage ist, das zuletzt aufgetretene Ereignis genau zu definieren.
Um die Funktion zu implementieren, fügen wir drei neue Methoden zum 'private' Bereich der Klasse für historische Aufträge und Deals hinzu:
//--- Liefert das Flag des Auftragsobjekts nach Typ und Ticket in der Liste der historischen Aufträge und Deals. bool IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type); //--- Rückgabe des "verlorenen" Auftragstyps und des Tickets ulong OrderSearch(const int start,ENUM_ORDER_TYPE &order_type); //--- Erstellen der Auftragsobjektes und Eintragen in der Liste bool CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type);
und ihre Umsetzung auch außerhalb des Klassenkörpers.
Die Methode gibt das Flag des in der Liste vorhandenen Auftragsobjekts durch sein Ticket und seinen Typ zurück:
//+-----------------------------------------------------------------------------+ //| Liefert das Flag des aktuellen Auftragsobjektes der Liste nach Typ & Ticket | //+-----------------------------------------------------------------------------+ bool CHistoryCollection::IsPresentOrderInList(const ulong order_ticket,const ENUM_ORDER_TYPE type) { CArrayObj* list=dynamic_cast<CListObj*>(&this.m_list_all_orders); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,type,EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,order_ticket,EQUAL); return(list.Total()>0); } //+------------------------------------------------------------------+
Den Zeiger auf die Liste mit dem dynamischen Typecasting erstellen
(senden Sie die CArrayObj-Liste an die Klasse CSelect, während Kollektionslisten vom Typ CListObj sind und von CArrayObj abgeleitet werden)
Nur
die Aufträge behalten , die den Typ haben, der der Methode durch die
Eingabe übergeben wurde.
Nur den Auftrag behalten ,
der das Ticket hat, das der Methode übergeben wurde.
Wenn ein solcher
Auftrag existiert (die Liste ist größer als Null), geben wir
true zurück.
Die Methode, die einen Typ und eine Methode eines Auftrags zurückgibt, der nicht der letzte in der Liste des Terminals, aber nicht in der Kollektionsliste ist:
//+------------------------------------------------------------------+ //| Liefert den Typ und das Ticket der "verlorenen" Auftrags zurück | //+------------------------------------------------------------------+ ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type) { ulong order_ticket=0; for(int i=start-1;i>=0;i--) { ulong ticket=::HistoryOrderGetTicket(i); if(ticket==0) continue; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE); if(this.IsPresentOrderInList(ticket,type)) continue; order_ticket=ticket; order_type=type; } return order_ticket; } //+------------------------------------------------------------------+
Der Index des letzten Auftrags wird an die
Terminal-Auftragsliste übergeben. Da der Index die bereits in der Kollektion vorhandene Aufträge angibt, sollte die
Suchschleife aus der vorherigen Reihenfolge in der Liste (start-1)
gestartet werden.
Da sich der benötigte Auftrag in der Regel am Ende der Liste befindet, suchen Sie mit der Methode IsPresentOrderInList() nach einer
Reihenfolge mit einem Ticket und einem in der Kollektion fehlenden Typ in der Schleife vom Listenende aus.
Wenn der Auftrag in der Kollektion vorhanden ist, überprüfen Sie den nächsten.
Sobald ein Auftrag in der Kollektion fehlt, wird dessen
Ticket und Typ
geschrieben und an das aufrufende Programm zurückgesendet. Das Ticket wird
durch die Methode result zurückgegeben, während der Typ in der
Variable über den Link zurückgegeben wird.
Da wir nun an mehreren Stellen innerhalb der Klasse Auftragsobjekte erstellen müssen (bei der Definition einer neuen Bestellung und bei der Suche nach einer "verlorenen"), machen wir eine separate Methode, um ein Auftragsobjekt zu erstellen und es in die Kollektionsliste aufzunehmen:
//+------------------------------------------------------------------+ //| Erstellen eines Auftragsobjekts und Platzieren in der Liste | //+------------------------------------------------------------------+ bool CHistoryCollection::CreateNewOrder(const ulong order_ticket,const ENUM_ORDER_TYPE order_type) { COrder* order=NULL; if(order_type==ORDER_TYPE_BUY) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_BUY_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_BUY_STOP) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_STOP) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } #ifdef __MQL5__ else if(order_type==ORDER_TYPE_BUY_STOP_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_SELL_STOP_LIMIT) { order=new CHistoryPending(order_ticket); if(order==NULL) return false; } else if(order_type==ORDER_TYPE_CLOSE_BY) { order=new CHistoryOrder(order_ticket); if(order==NULL) return false; } #endif if(this.m_list_all_orders.InsertSort(order)) return true; else { delete order; return false; } return false; } //+------------------------------------------------------------------+
Hier ist alles einfach und klar: Die Methode erhält Auftragsticket und -typ, und je nach Auftragsart wird ein neues Auftragsobjekt angelegt. Wenn das Objekt nicht erstellt werden konnte, wird sofort false zurückgegeben. Wenn das Objekt erfolgreich erstellt wurde, wird es in die Kollektion gestellt und true wird zurückgegeben. Wenn es nicht in die Kollektion platziert werden konnte, wird ein neu erstelltes Objekt entfernt und false zurückgegeben.
Ändern wir die Methode Refresh() der Klasse der Kollektion, da der "Verlust" eines benötigen Auftrags verarbeitet werden soll:
//+------------------------------------------------------------------+ //| Aktualisieren der Liste der Aufträge und Deals | //+------------------------------------------------------------------+ void CHistoryCollection::Refresh(void) { #ifdef __MQL4__ int total=::OrdersHistoryTotal(),i=m_index_order; for(; i<total; i++) { if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType(); //--- Geschlossenen Positionen und Salden-/Korrekturbuchungen if(order_type<ORDER_TYPE_BUY_LIMIT || order_type>ORDER_TYPE_SELL_STOP) { CHistoryOrder *order=new CHistoryOrder(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order)) { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list")); delete order; } } else { //--- Entfernte Pending-Order CHistoryPending *order=new CHistoryPending(::OrderTicket()); if(order==NULL) continue; if(!this.m_list_all_orders.InsertSort(order))this.m_list_all_orders.Type() { ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list")); delete order; } } } //--- int delta_order=i-m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; this.m_is_trade_event=(this.m_delta_order!=0 ? true : false); //--- __MQL5__ #else if(!::HistorySelect(0,END_TIME)) return; //--- Orders int total_orders=::HistoryOrdersTotal(),i=m_index_order; for(; i<total_orders; i++) { ulong order_ticket=::HistoryOrderGetTicket(i); if(order_ticket==0) continue; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE); if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY) { //--- Wenn es keinen Auftrag mit diesem Typ und diesem Ticket in der Liste gibt, wird ein Auftragsobjekt erstellt und in die Liste eingetragen if(!this.IsPresentOrderInList(order_ticket,type)) { if(!this.CreateNewOrder(order_ticket,type)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } //--- Gibt es einen solchen Auftrag in der Liste, heißt dass, der benötigte Auftrag ist nicht der letzte in der historischen Liste Let's find it else { ENUM_ORDER_TYPE type_lost=WRONG_VALUE; ulong ticket_lost=this.OrderSearch(i,type_lost); if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } } else { //--- Wenn es keine Pending-Order mit diesem Typ und diesem Ticket in der Liste gibt, wird ein Auftragsobjekt erstellt und in die Liste eingetragen if(!this.IsPresentOrderInList(order_ticket,type)) { if(!this.CreateNewOrder(order_ticket,type)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } //--- Gibt es einen solchen Auftrag in der Liste, heißt dass, der benötigte Auftrag ist nicht der letzte in der historischen Liste Let's find it else { ENUM_ORDER_TYPE type_lost=WRONG_VALUE; ulong ticket_lost=this.OrderSearch(i,type_lost); if(ticket_lost>0 && !this.CreateNewOrder(ticket_lost,type_lost)) ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to list")); } } } //--- Sichern des Index des zuletzt ergänzten Auftrags und der Änderung im Vergleich zur vorherigen Prüfung int delta_order=i-this.m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; //--- Deals int total_deals=::HistoryDealsTotal(),j=m_index_deal; for(; j<total_deals; j++) { ulong deal_ticket=::HistoryDealGetTicket(j); if(deal_ticket==0) continue; CHistoryDeal *deal=new CHistoryDeal(deal_ticket); if(deal==NULL) continue; if(!this.m_list_all_orders.InsertSort(deal)) { ::Print(DFUN,TextByLanguage("Не удалось добавить сделку в список","Could not add deal to list")); delete deal; } } //--- Sichern des Index des zuletzt ergänzten Deals und der Änderung im Vergleich zur vorherigen Prüfung int delta_deal=j-this.m_index_deal; this.m_index_deal=j; this.m_delta_deal=delta_deal; //--- Setzen des Flags für neue Ereignisse in der Historie this.m_is_trade_event=(this.m_delta_order+this.m_delta_deal); #endif } //+------------------------------------------------------------------+
Der Block zur Bearbeitung neuer Aufträge für MQL5 wurde in der Methode geändert. Alle vorgenommenen Änderungen werden durch Kommentare beschrieben und im aufgelisteten Text hervorgehoben.
Fügen wir die Methodendefinition hinzu, um nach ähnlichen Aufträgen im 'public' Bereich der Klasse COrder zu suchen:
//---- Vergleich von COrder nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen) bool IsEqual(COrder* compared_order) const;
und seine Implementierung auch außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Vergleichen von COrder-Objekte nach allen Eigenschaften | //+------------------------------------------------------------------+ bool COrder::IsEqual(COrder *compared_order) const { int beg=0, end=ORDER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } beg=end; end+=ORDER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } beg=end; end+=ORDER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i; if(this.GetProperty(prop)!=compared_order.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Die Methode prüft alle Eigenschaften des aktuellen Auftragsobjekts und die vom Zeiger
in einer Schleife der an die Methode übergebene verglichene Auftrag.
Sobald eine der Eigenschaften der aktuellen
Bestellung, die nicht gleich der gleichen Eigenschaft der verglichenen Bestellung ist, erkannt wird, wird
false zurückgegeben, was bedeutet, dass die Aufträge nicht gleich sind.
Ereignisklassen
Die Vorbereitungsphase ist abgeschlossen. Beginnen wir mit dem Erstellen von Klassen von Ereignisobjekten.
Wir werden genau das gleiche tun wie beim Anlegen von Auftragsklassen. Wir werden eine grundlegende Ereignisklasse und fünf Nachkommenklassen entwickeln, die durch ihre Zustände beschrieben werden:
- Ereignis der Positionseröffnung,
- Ereignis des Schließens einer Position,
- Ereignis des Platzierens einer Pending-Order,
- Ereignis zum Entfernen von Pending-Orders,
- Ereignis einer Saldooperation
Wir erstellen im zuvor erstellten Ordner Events des Bibliotheksverzeichnisses Objects eine neue Klasse CEvent, die von der Basisklasse CObject abgeleitet wird. In der neu erstellten Klassenvorlage erstellen wir die notwendigen Einbindungen der Servicefunktionendatei, der Klassen der Auftragskollektionen sowie der 'private' und 'protected' Klassenmitglieder und Methoden:
//+------------------------------------------------------------------+ //| Event.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" #property strict // Notwendig für mql4 //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Abstrakte Ereignisklasse | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Ereigniscode //--- Rückgabe des Arrayindex des Ereignisses mit den (1) Double- und (2) String-Eigenschaften int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Handelsereignis long m_chart_id; // Chart-ID des Steuerprogramms int m_digits_acc; // Dezimalstellen der Kontowährung long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Integer-Eigenschaften des Ereignisses double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Double-Eigenschaften des Ereignisses string m_string_prop[EVENT_PROP_STRING_TOTAL]; // String-Eigenschaften des Ereignisses //--- Rückgabe des Vorhandenseins des Flags des Handelsereignisses bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- 'Protected' Konstruktor CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Standardmäßiger Konstruktor CEvent(void){;} //--- Setzen der (1) Integer-, (2) Double- und (3) String-Eigenschaften des Ereignisses void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Rückgabe von (1) Integer-, (2) Double- und (3) String-Eigenschaften aus dem Array der Eigenschaften long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Rückgabe des Flags des Ereignisses, das die Eigenschaften unterstützt virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Setzen der Chart-ID des Steuerprogramms void SetChartID(const long id) { this.m_chart_id=id; } //--- Dekodieren des Ereigniscodes und setzen des Handelsereignisses, (2) Rückgabe des Handelsereignisses void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Senden des Ereignisses an das Chart (Implementation in der abgeleiteten Klasse) virtual void SendEvent(void) {;} //--- Vergleichen der CEvent-Objekte nach der angegebenen Eigenschaft (für das Sorteiren der Liste nach der angegebenen Eigenschaft des Ereignisobjektes) virtual int Compare(const CObject *node,const int mode=0) const; //---- Vergleich von CEvent-Objekten nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen) bool IsEqual(CEvent* compared_event) const; //+------------------------------------------------------------------+ //| Methode für einen vereinfachten Zugriff auf die Eigenschaften | //+------------------------------------------------------------------+ //--- Rückgabe von (1) Ereignis-Typ, (2) Ereignis-Zeit in Millisekunden, (3) Ereignis-Status, (4) Ereignis-Grund, (5) Dealtyp, (6) Deal-Ticket, //--- (7) Auftragstyp des dadurch geöffneten Deal-Ereignisses, (8) Auftragstyp der Positionseröffnung, (9) letztes Auftragsticket der Position, //--- (10) erstes Auftragsticket der Position, (11) Positions-ID, (12) ID der entgegengesetzten Position, (13) Magicnummer, (14) Eröffnungszeit der Position ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } long TypeDeal(void) const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } long TypeOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } long TypeOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- Rückgabe von (1) Preis bei den ein Ereignis auftritt, (2) Eröffnungspreis, (3) Schließpreis, //--- (4) StopLoss, (5) TakeProfit, (6) Gewinn, (7) verlangtes Volumen, (8), ausgeführtes Volumen, (9) verbliebenes Volumen double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL); } double VolumeExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED); } double VolumeCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT); } //--- Rückgabe eines Symbols string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Beschreibung der Objekteigenschaften des Auftrags | //+------------------------------------------------------------------+ //--- Rückgabe der Beschreibung der (1) Integer-, (2) Double- und (3) String-Eigenschaft des Auftrages string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Rückgabe von (1) Status und (2) Typ des Ereignisses string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Rückgabe von (1) Order/Deal/Position, (2) übergeordnete Order, (3) Position string TypeOrderDescription(void) const; string TypeOrderBasedDescription(void) const; string TypePositionDescription(void) const; //--- Rückgabe des Namens des Grundes von Order/Deal/Position string ReasonDescription(void) const; //--- Anzeigen (1) Beschreibung der Auftragseigenschaften (full_prop=true - alle Eigenschaften, false - nur die unterstützten), //--- (2) kurze Ereignisnachrichten (implementiert in abgeleiteten Klassen) im Journal void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Der Konstruktor empfängt den Ereignisstatus,
Handelsereigniscode und Auftrags-
oder Dealticket, das das Ereignis ausgelöst hat.
Hier ist fast alles einem geschützten Konstruktor der zuvor betrachteten Klasse COrder im ersten Teil der Bibliotheksbeschreibung ähnlich.
Der Unterschied besteht darin, dass im geschützten Klassenkonstruktor
nur zwei Ereignis-Eigenschaften gefüllt werden. Dies sind der Ereignisstatus und das Ticket eines Auftrags/Deal, der das Ereignis
ausgelöst hat. Der Ereignistyp wird erkannt und in der Klassenmethode SetTypeEvent() gespeichert, basierend auf dem an den Konstruktor
übergebenen Ereigniscode. Alle anderen Ereigniseigenschaften werden aus dem Status von Aufträgen und Deals, die am Ereignis beteiligt
sind, erkannt und durch die entsprechenden Klassenmethoden separat gesetzt. Dies geschieht, weil Ereignisse in der Klasse der
Ereignis-Kollektion mit der Methode, die alle Eigenschaften für ein neu erzeugtes Ereignis festlegt, erkannt werden sollen.
Wir haben den Ereigniscode (m_event_code), sowie seine
Füllung und Interpretation im
vierten Teil der Bibliotheksbeschreibung berücksichtigt. Wir haben es hier
aus der Klasse CEngine verschoben, da es temporär in die Bibliotheksbasisklasse platziert wurde, um die Arbeit mit Ereignissen zu
überprüfen. Jetzt wird es in der Klasse der Ereigniskollektion berechnet und beim Anlegen eines Ereignisobjekts an den
Klassenkonstrukteur übergeben.
Das Handelsereignis (m_trade_event) selbst wird
durch Dekodierung des Ereigniscodes in der Methode SetTypeEvent() verarbeitet. Wir haben die Dekodierungsmethode des Ereigniscodes
bereits im
vierten Artikel beschrieben.
Wir benötigen die Chart
ID des Steuerprogramms (
m_chart_id), um nutzerdefinierte Nachrichten über Ereignisse an sie zu senden.
Die
Anzahl der Nachkommastellen für die Kontowährung (
m_digit_acc) ist notwendig für die korrekte Anzeige von Meldungen über Ereignisse im Journal.
Die Methoden zum Vergleichen der Compare() und IsEqual() Objektereignisseigenschaften sind recht einfach und klar. Wir haben die Methode Compare() im ersten Teil der Bibliotheksbeschreibung berücksichtigt. Sie war ähnlich wie der des Objekts COrder. Im Vergleich zu der ersten Methode, die zwei Objekte nur durch eine der Eigenschaften vergleicht, vergleicht IsEqual() alle Felder von beiden Objekten. Wenn alle Felder der beiden Objekte gleich sind (jede Eigenschaft des aktuellen Objekts ist gleich der entsprechenden Eigenschaft des verglichenen Objekts), dann sind beide Objekte identisch. Die Methode überprüft alle Eigenschaften der beiden Objekte in einer Schleife und gibt false zurück, sobald ein Unterschied erkannt wird. Weitere Prüfungen machen keinen Sinn, da eine der Objekteigenschaften nicht länger gleich der entsprechenden Eigenschaft des verglichenen Objekts ist.
//+------------------------------------------------------------------+ //| Vergleich einer bestimmten Eigenschaft von CEvent-Objekten | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- Vergleich der Integer-Eigenschaften von zwei Ereignissen if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- Vergleich der Integer-Eigenschaften von zwei Objekten if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- Vergleich der String-Eigenschaften von zwei Objekten else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Vergleichen der Ereignisse CEvent nach allen Eigenschaften | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) const { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Lassen Sie uns einen genaueren Blick auf die Methode SetTypeEvent() werfen.
Alle notwendigen Prüfungen und Aktionen werden direkt in den Codekommentaren festgelegt:
//+------------------------------------------------------------------+ //| Dekodieren des Ereigniscodes und setzen des Handelsereignisses | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Pending-Order platziert (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending-Order entfernt (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Positionseröffnung (Prüfen mehrerer Flags im Ereigniscode) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- Wenn die Pending-Order durch den Preis aktiviert wurde if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Pending-Order aktiviert" oder "Pending-Order teilweise aktiviert"" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Prüfen des Flags für ein teilweises Eröffnen und setzen des Handelsereignisses von "Position eröffnet" oder "Position teilweise eröffnet" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Position geschlossen (Prüfen mehrerer Flags im Ereigniscode) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- wenn eine Position durch StopLoss geschlossen wurde if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch StopLoss" oder "Position teilweise geschlossen durch StopLoss" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn die Position durch TakeProfit geschlossen wurde else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch TakeProfit" oder "Position teilweise geschlossen durch TakeProfit" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn eine Position durch eine Gegenposition geschlossen wurde else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch eine Gegenposition" oder "Position teilweise geschlossen durch eine Gegenposition" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn eine Position geschlossen wurde else { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen" oder "Position teilweise geschlossen" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Saldooperation auf dem Konto (Klärung des Ereignisses durch den Dealtyp) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Initialisierung des Handelsereignisses this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Nehmen des Dealtyps ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- Wenn der Deal eine Saldenoperationen ist if(deal_type==DEAL_TYPE_BALANCE) { //--- Prüfen des Deals-Gewinns und setzen des Ereignisses (Gelder zu- oder abbuchen) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- Buchungstyp des verbliebenen Saldos passt zur Enumeration ENUM_DEAL_TYPE beginnend mit DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- Setzen des Ereignisses this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+
Hier ist alles einfach: Ein Ereigniscode wird an die Methode übergeben und die Ereigniscodeflags werden dann überprüft. Wenn der Code das Häkchen hat, wird das entsprechende Handelsereignis eingetragen. Da der Ereigniscode mehrere Flags haben kann, werden alle möglichen Flags für das Ereignis geprüft und der Ereignistyp aus seiner Kombination definiert. Anschließend wird der Ereignistyp in die entsprechende Klassenvariable aufgenommen und in die Eigenschaft des Ereignisobjekts (EVENT_PROP_TYPE_EVENT) eingetragen.
Lassen Sie uns einen Blick auf die Auflistung der übrigen Klassenmethoden werfen:
//+------------------------------------------------------------------+ //| Liefert die Beschreibung der Integer-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position open time")+": "+TimeMSCtoString(this.GetProperty(property)) : "" ); } //+------------------------------------------------------------------+ //| Rückgabe der Beschreibung der Double-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_INITIAL ? TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_EXECUTED ? TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_CURRENT ? TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : "" ); } //+------------------------------------------------------------------+ //| Liefert die Beschreibung der String-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""; } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Ereignis-Status | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position opened") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : "" ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Handelsereignisses | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position opened") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position opened partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED ? TextByLanguage("Разворот позиции","Position reversal") : event==TRADE_EVENT_POSITION_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens von Order/Deal/Position | //+------------------------------------------------------------------+ string CEvent::TypeOrderDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : "Unknown" ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des übergeordneten Auftrages | //+------------------------------------------------------------------+ string CEvent::TypeOrderBasedDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Rückgabe des Positionsnamen | //+------------------------------------------------------------------+ string CEvent::TypePositionDescription(void) const { ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); return PositionTypeDescription(type); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Grundes von Order/Deal/Position | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Запрос выполнен полностью","Request fully executed") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Запрос выполнен частично","Request partially executed") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+ //| Anzeige der Ereigniseigenschaften im Journal | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
Die Logik all dieser Methoden ist ähnlich wie die der bereits beschriebenen Auftragsdatenausgabemethoden. Deshalb werden wir uns nicht auf sie konzentrieren, da hier alles ganz einfach und visuell verständlich ist.
Die vollständige Liste der Eventklassen:
//+------------------------------------------------------------------+ //| Event.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" #property strict // Notwendig für mql4 //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Abstrakte Ereignisklasse | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Ereigniscode //--- Rückgabe des Arrayindex des Ereignisses mit den (1) Double- und (2) String-Eigenschaften int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Handelsereignis long m_chart_id; // Chart-ID des Steuerprogramms int m_digits_acc; // Dezimalstellen der Kontowährung long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Integer-Eigenschaften des Ereignisses double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Double-Eigenschaften des Ereignisses string m_string_prop[EVENT_PROP_STRING_TOTAL]; // String-Eigenschaften des Ereignisses //--- Rückgabe des Vorhandenseins des Flags des Handelsereignisses bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- 'Protected' Konstruktor CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Standardmäßiger Konstruktor CEvent(void){;} //--- Setzen der (1) Integer-, (2) Double- und (3) String-Eigenschaften des Ereignisses void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Rückgabe von (1) Integer-, (2) Double- und (3) String-Eigenschaften aus dem Array der Eigenschaften long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Rückgabe des Flags des Ereignisses, das die Eigenschaften unterstützt virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Setzen der Chart-ID des Steuerprogramms void SetChartID(const long id) { this.m_chart_id=id; } //--- Dekodieren des Ereigniscodes und setzen des Handelsereignisses, (2) Rückgabe des Handelsereignisses void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Senden des Ereignisses an das Chart (Implementation in der abgeleiteten Klasse) virtual void SendEvent(void) {;} //--- Vergleichen der CEvent-Objekte nach der angegebenen Eigenschaft (für das Sorteiren der Liste nach der angegebenen Eigenschaft des Ereignisobjektes) virtual int Compare(const CObject *node,const int mode=0) const; //---- Vergleich von CEvent-Objekten nach allen Eigenschaften (um nach gleichen Ereignisobjekten zu suchen) bool IsEqual(CEvent* compared_event); //+------------------------------------------------------------------+ //| Methode für einen vereinfachten Zugriff auf die Eigenschaften | //+------------------------------------------------------------------+ //--- Rückgabe von (1) Ereignis-Typ, (2) Ereignis-Zeit in Millisekunden, (3) Ereignis-Status, (4) Ereignis-Grund, (5) Dealtyp, (6) Deal-Ticket, //--- (7) Auftragstyp des dadurch geöffneten Deal-Ereignisses, (8) Auftragstyp der Positionseröffnung, (9) letztes Auftragsticket der Position, //--- (10) erstes Auftragsticket der Position, (11) Positions-ID, (12) ID der entgegengesetzten Position, (13) Magicnummer, (14) Eröffnungszeit der Position ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } long TypeDeal(void) const { return this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } long TypeOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } long TypeOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- Rückgabe von (1) Preis bei den ein Ereignis auftritt, (2) Eröffnungspreis, (3) Schließpreis, //--- (4) StopLoss, (5) TakeProfit, (6) Gewinn, (7) verlangtes Volumen, (8), ausgeführtes Volumen, (9) verbliebenes Volumen double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_INITIAL); } double VolumeExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_EXECUTED); } double VolumeCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_CURRENT); } //--- Rückgabe eines Symbols string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Beschreibung der Objekteigenschaften des Auftrags | //+------------------------------------------------------------------+ //--- Rückgabe der Beschreibung der (1) Integer-, (2) Double- und (3) String-Eigenschaft des Auftrages string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Rückgabe von (1) Status und (2) Typ des Ereignisses string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Rückgabe von (1) Order/Deal/Position, (2) übergeordnete Order, (3) Position string TypeOrderDescription(void) const; string TypeOrderBasedDescription(void) const; string TypePositionDescription(void) const; //--- Rückgabe des Namens des Grundes von Order/Deal/Position string ReasonDescription(void) const; //--- Anzeigen (1) Beschreibung der Auftragseigenschaften (full_prop=true - alle Eigenschaften, false - nur die unterstützten), //--- (2) kurze Ereignisnachrichten (implementiert in abgeleiteten Klassen) im Journal void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+ //| Vergleich einer bestimmten Eigenschaft von CEvent-Objekten | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- Vergleich der Integer-Eigenschaften von zwei Ereignissen if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- Vergleich der Double-Eigenschaften von zwei Ereignisses if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- Vergleich der String-Eigenschaften von zwei Ereignissen else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Vergleichen von COrder-Objekte nach allen Eigenschaften | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } //--- return true; } //+------------------------------------------------------------------+ //| Dekodieren des Ereigniscodes und setzen des Handelsereignisses | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Pending-Order platziert (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending-Order entfernt (Prüfen der Übereinstimmung des Ereigniscodes, da es hier nur ein Flag geben kann) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Positionseröffnung (Prüfen mehrerer Flags im Ereigniscode) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- Wenn die Pending-Order durch den Preis aktiviert wurde if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Pending-Order aktiviert" oder "Pending-Order teilweise aktiviert"" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Prüfen des Flags für ein teilweises Eröffnen und setzen des Handelsereignisses von "Position eröffnet" oder "Position teilweise eröffnet" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Position geschlossen (Prüfen mehrerer Flags im Ereigniscode) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- wenn eine Position durch StopLoss geschlossen wurde if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch StopLoss" oder "Position teilweise geschlossen durch StopLoss" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn die Position durch TakeProfit geschlossen wurde else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch TakeProfit" oder "Position teilweise geschlossen durch TakeProfit" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn eine Position durch eine Gegenposition geschlossen wurde else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen durch eine Gegenposition" oder "Position teilweise geschlossen durch eine Gegenposition" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Wenn eine Position geschlossen wurde else { //--- Prüfen des Flags für ein teilweises Schließen und setzen des Handelsereignisses von "Position geschlossen" oder "Position teilweise geschlossen" this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Saldooperation auf dem Konto (Klärung des Ereignisses durch den Dealtyp) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Initialisierung des Handelsereignisses this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Nehmen des Dealtyps ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- Wenn der Deal eine Saldenoperationen ist if(deal_type==DEAL_TYPE_BALANCE) { //--- Prüfen des Deals-Gewinns und setzen des Ereignisses (Gelder zu- oder abbuchen) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- Buchungstyp des verbliebenen Saldos passt zur Enumeration ENUM_DEAL_TYPE beginnend mit DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- Setzen des Ereignisses this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+ //| Liefert die Beschreibung der Integer-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property)) : "" ); } //+------------------------------------------------------------------+ //| Rückgabe der Beschreibung der Double-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_INITIAL ? TextByLanguage("Начальный объём","Initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_EXECUTED ? TextByLanguage("Исполненный объём","Executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_CURRENT ? TextByLanguage("Оставшийся объём","Remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : "" ); } //+------------------------------------------------------------------+ //| Liefert die Beschreibung der String-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""; } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Ereignisstatus | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position opened") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : "" ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Handelsereignisses | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position opened") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position opened partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED ? TextByLanguage("Разворот позиции","Position reversal") : event==TRADE_EVENT_POSITION_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens von Order/Deal/Position | //+------------------------------------------------------------------+ string CEvent::TypeOrderDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : "Unknown" ); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des übergeordneten Auftrages | //+------------------------------------------------------------------+ string CEvent::TypeOrderBasedDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Rückgabe des Positionsnamen | //+------------------------------------------------------------------+ string CEvent::TypePositionDescription(void) const { ENUM_POSITION_TYPE type=PositionTypeByOrderType((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); return PositionTypeDescription(type); } //+------------------------------------------------------------------+ //| Rückgabe des Namens des Grundes von Order/Deal/Position | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Запрос выполнен полностью","Request fully executed") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Запрос выполнен частично","Request partially executed") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+ //| Anzeige der Ereigniseigenschaften im Journal | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
Die Klasse des abstrakten Basisereignisses ist fertig. Jetzt müssen wir fünf abgeleitete Klassen erstellen, die ein Ereignis sind und ihren Typ angeben: Platzieren einer Pending-Order, Löschen einer Pending-Order, Öffnen einer Position, Schließen einer Position und eine Saldooperation.
Erstellen wir eine abgeleitete Klasse mit dem Ereignisstatus "Pending-Order platzieren".
Im Ordner Events Library erstellen wir eine neue Datei der Klasse CEventOrderPlased EventOrderPlased.mqh mit der Basisklasse CEvent und fügen alle notwendigen Verbindungen und Methoden hinzu:
//+------------------------------------------------------------------+ //| EventOrderPlased.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Platzieren des Ereignisses einer Pending-Order | //+------------------------------------------------------------------+ class CEventOrderPlased : public CEvent { public: //--- Konstructor CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {} //--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Übergeben Sie den Ereigniscode und das Ticket eines Auftrags oder Deals, das das Ereignis ausgelöst hat, an den Klassenkonstruktor und senden Sie den Ereignisstatus "Platzieren einer Pending-Order". (EVENT_STATUS_MARKET_PENDING), den Ereigniscode und ein Auftrags- oder Dealticket an die übergeordnete Klasse in der Initialisierungsliste:
CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket) {}
Wir haben die Methoden, die die Flags eines Objekts zurückgeben, das bestimmte Eigenschaften von SupportProperty() unterstützt, bereits im ersten Teil der Bibliotheksbeschreibung beschrieben. Hier ist alles gleich:
//+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_DEAL_EVENT || property==EVENT_PROP_TICKET_DEAL_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_POSITION_BY_ID || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Double-Eigenschaft, sonst 'false' | //+------------------------------------------------------------------+ bool CEventOrderPlased::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+
Das übergeordnete Ereignisobjekt CEvent verfügt über die Methode Print(), die vollständige Daten über alle unterstützten
Ereignisobjekteigenschaften anzeigt, und die virtuelle Methode PrintShort(), die es ermöglicht, ausreichende Daten über das Ereignis
im Terminaljournal in zwei Zeilen anzuzeigen.
Die Implementierung der Methode PrintShort() in jeder abgeleiteten Klasse des Basis-Ereignisobjekts ist individuell, da sich
Ereignisse auch in ihrer Herkunft unterscheiden:
//+------------------------------------------------------------------+ //| Eine kurze Ereignismeldung im Journal anzeigen | //+------------------------------------------------------------------+ void CEventOrderPlased::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+
Hier machen wir Folgendes:
- Wie erstellen einen Nachrichtenkopf, der aus einer Beschreibung des Ereignistyps und der Uhrzeit besteht.
- Wenn der Auftrag einen StopLoss hat, erstellen wir die Zeile mit ihrer Beschreibung, sonst bleibt die Zeichenkette leer.
- Wenn der Auftrag einen TakeProfit hat, erstellen wir die Zeile mit ihrer Beschreibung, sonst bleibt die Zeichenkette leer.
- Erstellen wir eine Zeile mit dem Auftragsvolumen.
- Wenn der Auftrag eine Magicnummer hat, erstellen wir die Zeile mit ihrer Beschreibung, sonst bleibt die Zeichenkette leer.
- Erstellen wir eine Zeile mit Angabe des Auftragstyps und des Tickets.
- Erstellen wir eine Zeile mit dem ´Preis des Auftrags und dem Symbol, mit dem der Auftrag platziert wird.
- Erstellen wir eine vollständige Zeile aus allen oben genannten Beschreibungen.
- Ausdrucken der erstellten Zeile im Journal
Die Methode, ein nutzerdefiniertes Ereignis an das Diagramm zu senden, ist recht einfach:
//+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventOrderPlased::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Zuerst wird eine kurze Nachricht über das Ereignis im Journal
angezeigt, dann wird das nutzerdefinierte Ereignis EventChartCustom()
an die Chart-ID m_chart_id der Basis-Event-Klasse CEvent
gesendet.
Senden des Ereignisses m_trade_event an die Event ID,
Auftragsticket — zum long
Parametertyp,
Auftragspreis — zum Double
Parametertyp,
Auftragssymbol — zum string
Parametertyp.
Die Klasse für die Anzeige von Nachrichten, die es n ermöglicht, Nachrichtenebenen für die Anzeige nur notwendiger Daten im Journal festzulegen, soll in Zukunft entwickelt werden. Im aktuellen Entwicklungsstadium der Bibliothek werden standardmäßig alle Meldungen angezeigt.
Betrachten wir die vollständige Liste der anderen Ereignisklassen.
"Entfernen von Pending-Orders":
//+------------------------------------------------------------------+ //| EventOrderRemoved.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Ereignis des Platzierens einer Pending-Order | //+------------------------------------------------------------------+ class CEventOrderRemoved : public CEvent { public: //--- Konstructor CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket) {} //--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_DEAL_EVENT || property==EVENT_PROP_TICKET_DEAL_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Double-Eigenschaft, sonst 'false' | //+------------------------------------------------------------------+ bool CEventOrderRemoved::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return(property==EVENT_PROP_PROFIT ? false : true); } //+------------------------------------------------------------------+ //| Eine kurze Ereignismeldung im Journal anzeigen | //+------------------------------------------------------------------+ void CEventOrderRemoved::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypeOrderDescription()+" #"+(string)this.TicketOrderEvent(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventOrderRemoved::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Ereignisklasse "Position eröffnen":
//+------------------------------------------------------------------+ //| EventPositionOpen.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Ereignis einer Positionseröffnung | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { public: //--- Konstructor CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return(property==EVENT_PROP_POSITION_BY_ID ? false : true); } //+------------------------------------------------------------------+ //| Rückgabe von 'true' bei Unterstützung der übergebenen | //| Double-Eigenschaft, sonst 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Eine kurze Ereignismeldung im Journal anzeigen | //+------------------------------------------------------------------+ void CEventPositionOpen::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string order=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? " #"+(string)this.TicketOrderPosition() : ""); string activated=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED) ? TextByLanguage(" активацией ордера "," by ")+this.TypeOrderBasedDescription() : ""); string sl=(this.PriceStopLoss()>0 ? ", sl "+::DoubleToString(this.PriceStopLoss(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+::DoubleToString(this.PriceTakeProfit(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)) : ""); string vol=::DoubleToString(this.VolumeInitial(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypePositionDescription()+" #"+(string)this.PositionID(); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceOpen(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string txt=head+this.Symbol()+" "+vol+" "+type+activated+order+price+sl+tp+magic; ::Print(txt); } //+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventPositionOpen::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+
Ereignisklasse "Position schließen":
//+------------------------------------------------------------------+ //| EventPositionClose.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Ereignis einer Positionseröffnung | //+------------------------------------------------------------------+ class CEventPositionClose : public CEvent { public: //--- Konstructor CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {} //--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Double-Eigenschaft, sonst 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Anzeige einer kurzen Nachricht über das Ereignis im Journal | //+------------------------------------------------------------------+ void CEventPositionClose::PrintShort(void) { string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string opposite=(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS) ? " by "+this.TypeOrderDescription()+" #"+(string)this.PositionByID() : ""); string vol=::DoubleToString(this.VolumeExecuted(),DigitsLots(this.Symbol())); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string type=this.TypePositionDescription()+" #"+(string)this.PositionID()+opposite; string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceClose(),(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS)); string profit=TextByLanguage(", профит: ",", profit: ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); string txt=head+this.Symbol()+" "+vol+" "+type+price+magic+profit; ::Print(txt); } //+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventPositionClose::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol()); } //+------------------------------------------------------------------+
Ereignisklasse "Saldooperation":
//+------------------------------------------------------------------+ //| EventBalanceOperation.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Ereignis einer Positionseröffnung | //+------------------------------------------------------------------+ class CEventBalanceOperation : public CEvent { public: //--- Konstructor CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket) {} //--- Unterstützte (1) Double- und (2) Integer-Eigenschaften des Auftrags virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_ORDER_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_EVENT || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_POSITION_BY_ID || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_MAGIC_ORDER || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Double-Eigenschaft, sonst 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return(property==EVENT_PROP_PROFIT ? true : false); } //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| String-Eigenschaften, sonst 'false' | //+------------------------------------------------------------------+ bool CEventBalanceOperation::SupportProperty(ENUM_EVENT_PROP_STRING property) { return false; } //+------------------------------------------------------------------+ //| Anzeige einer kurzen Nachricht über das Ereignis im Journal | //+------------------------------------------------------------------+ void CEventBalanceOperation::PrintShort(void) { string head="- "+this.StatusDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; ::Print(head+this.TypeEventDescription()+": "+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY)); } //+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventBalanceOperation::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TypeEvent(),this.Profit(),::AccountInfoString(ACCOUNT_CURRENCY)); } //+------------------------------------------------------------------+
Wie wir aus den Listen ersehen können, unterscheiden sich die Klassen nur durch die Anzahl der unterstützten Eigenschaften, den Status, der an den übergeordneten Klassenkonstruktor gesendet wird, und die Methoden PrintShort(), da jedes Ereignis seine eigenen Funktionen hat, die für das Journal bestimmt sind. All dies kann aus den Listen der Methoden verstanden werden, und Sie können sie selbst analysieren, so dass es keinen Sinn macht, sich mit ihnen zu beschäftigen. Kommen wir zur Entwicklung der Klasse der Kollektion der Ereignisse.
Kollektion von Handelsereignissen
Im vierten Teil der Bibliotheksbeschreibung haben wir getestet, um
Kontoereignisse und deren Anzeige im Journal und im EA zu definieren. Allerdings konnten wir nur das letzte Ereignis verfolgen.
Außerdem befand sich die gesamte Funktionalität in der Basisklasse der Bibliothek CEngine.
Die richtige Entscheidung ist, alles in eine eigene Klasse zu stellen und alle darin auftretenden Ereignisse zu verarbeiten.
Um dies zu erreichen, habe ich Ereignisobjekte entwickelt. Jetzt müssen wir die Klasse dazu bringen, eine beliebige Anzahl von
gleichzeitig aufgetretenen Ereignissen zu behandeln. Schließlich kann es zu einer Situation kommen, in der Pending-Orders entfernt
oder platziert werden oder mehrere Positionen gleichzeitig in einer einzigen Schleife geschlossen werden.
Das Prinzip, das wir bereits in solchen Situationen getestet haben, würde uns nur das jüngste von mehreren auf einmal durchgeführten
Ereignissen geben. Ich denke, das ist falsch. Lassen Sie uns daher die Klasse, die alle Ereignisse, die auf einmal stattgefunden haben,
speichert, in die Liste der Ereigniskollektionen aufnehmen. Außerdem wird es in Zukunft möglich sein, mit den Methoden dieser Klassen
durch die Historie des Kontos zu gehen und alles wiederherzustellen, was seit seiner Eröffnung auf ihm passiert ist.
Erstellen wir in den DoEasy\Collections die neue Datei der Klasse CEventsCollection namens EventsCollection.mqh. Die Klasse CListObj sollte zur Basisklasse gemacht werden.
Füllen Sie die neu erstellte Klassenvorlage mit allen notwendigen Einbindungen, Mitgliedern und Methoden sofort aus:
//+------------------------------------------------------------------+ //| EventsCollection.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\EventBalanceOperation.mqh" #include "..\Objects\Events\EventOrderPlaced.mqh" #include "..\Objects\Events\EventOrderRemoved.mqh" #include "..\Objects\Events\EventPositionOpen.mqh" #include "..\Objects\Events\EventPositionClose.mqh" //+------------------------------------------------------------------+ //| Kollektion der Kontoereignisse | //+------------------------------------------------------------------+ class CEventsCollection : public CListObj { private: CListObj m_list_events; // Liste der Ereignisse bool m_is_hedge; // Flag des Hedging-Kontos long m_chart_id; // Chart-ID des Steuerprogramms ENUM_TRADE_EVENT m_trade_event; // Handelsereignis auf dem Konto CEvent m_event_instance; // Ereignisobjekt für die Suche nach einer Eigenschaft //---- Erstellen eines Handelsereignisses in Abhängigkeit vom Auftragsstatus. void CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market); //--- Auswahl und Rückgabe der Liste der Pending-Marktorder CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Auswahl und Rückgabe der Liste der histoirisch (1) entfernten Pending-Orders, (2) Deals, (3) alle Schließaufträge CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListCloseByOrders(CArrayObj* list); //--- Auswahl und Rückgabe der Liste von (1) allen Positionsaufträgen mach deren ID, (2) alle Deal-Positionen nach deren ID //--- (3) alle Deals der Markteintritte nach der Positions-ID, (4) alle Deals der Marktaustritte nach ID CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Rückgabe des Gesamtvolumens aller Deals (1) IN, (2) OUT der Position nach deren ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Rückgabe des (1) ersten, (2) letzten und (3) schließenden Auftrags aus der Liste aller Positionsaufträge, (4) ein Auftrag nach Ticket COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetOrderByTicket(CArrayObj* list,const ulong order_ticket); //--- Rückgabe des Flags des Ereignisobjekts in der Ereignisliste bool IsPresentEventInList(CEvent* compared_event); public: //--- Auswählen der Ereignisse aus der Kollektion mit einer Zeit im Bereich von begin_time bis end_time. CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Rückgabe des der gesamten Kollektionsliste des Ereignisses "wie besehen" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Aktualisieren der Ereignisliste void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Setzen der Chart-ID des Steuerprogramms void SetChartID(const long id) { this.m_chart_id=id; } //--- Rückgabe des letzten Handelsereignisses auf dem Konto ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Rücksetzen des letzten Handelsereignisses void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Konstructor CEventsCollection(void); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT) { this.m_list_events.Clear(); this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT); this.m_list_events.Type(COLLECTION_EVENTS_ID); this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Rücksetzen des Handelsereignisses in der
Initialisierungsliste des Klassenkonstruktors,
Löschen der Kollektionsliste im Konstruktorkörper,
Sortieren nach der Ereigniszeit,
Zuweisen der ID der
Ereignis-Kollektionsliste,
Zuweisen des Flags für Hedge-Konten
und
Zuweisen der Chart-ID des aktuellen Charts des Steuerprogramms.
Betrachten wir die Methoden, die notwendig sind, damit die Klasse funktioniert.
Die folgenden Klassenmitglieder werden im Abschnitt 'private' der Klasse deklariert:
CListObj m_list_events; // Ereignisliste bool m_is_hedge; // Flag des Hedging-Kontos long m_chart_id; // Chart-ID des Steuerprogramms ENUM_TRADE_EVENT m_trade_event; // Handelsereignis des Kontos CEvent m_event_instance; // Ereignisobjekt für die Suche nach einer Eigenschaft
Die Ereignisliste m_list_eventsbasiert
auf CListObj. Es speichert Ereignisse, die auf dem Konto seit dem Start des Programms auftreten. Außerdem werden wir es verwenden, um die
notwendige Anzahl von mehreren Ereignissen zu erhalten, die auf einmal aufgetreten sind.
Das Hedge Account Flagm_is_hedge
dient zum Speichern und Empfangen der Kontoart. Der Flag-Wert (Kontoart) definiert den Block, der die auf dem Chart auftretenden Ereignisse
behandelt
Die Chart-ID m_chart_idempfängt
nutzerdefinierte Ereignisse, die auf dem Konto auftreten. Die ID wird an Ereignisobjekte und zurück an das Chart gesendet. Die ID kann vom
Steuerungsprogramm aus mit der dafür geschaffenen Methode eingestellt werden.
Das Handelsereignis m_trade_eventspeichert
das letzte Ereignis auf dem Konto.
Das Ereignisobjekt m_event_instance
dient zur Suche nach einer Eigenschaft — ein spezielles Beispielobjekt zur internen Verwendung in der Methode, das die Liste der Ereignisse mit
angegebenen Daten des Suchbereichsbeginns und -endes zurückgibt. Wir haben bereits eine ähnliche Methode im
dritten Teil der Bibliotheksbeschreibung analysiert, um zu
diskutieren, wie man die Suche in den Listen nach verschiedenen Kriterien anordnet.
Hier im 'private' Bereich sehen Sie die für die Klassenoperation notwendigen Methoden:
//---- Erstellen eines Handelsereignisses in Abhängigkeit vom Auftragsstatus. void CreateNewEvent(COrder* order,CArrayObj* list_history); //--- Auswahl und Rückgabe der Liste der Pending-Marktorder CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Auswahl und Rückgabe der Liste der historischen (1) Aufträge, (2) entfernter Pending-Orders //--- (3) Deals, (4) alle Positionsaufträge nach deren ID, (5) all Positionsdeals nach deren ID //--- (6) alle Deals des Markteintritts nach der Positions-ID, (7) alle Deals des Marktverlassens nach deren Positions-ID //--- (7) alle Aufträge zum Schließen CArrayObj* GetListHistoryOrders(CArrayObj* list); CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllCloseByOrders(CArrayObj* list); //--- Rückgabe des Gesamtvolumens aller Deals (1) IN, (2) OUT der Position nach deren ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Rückgabe des (1) ersten, (2) letzten und (3) schließenden Auftrags aus der Liste aller Positionsaufträge, (4) ein Auftrag nach Ticket COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetOrderByTicket(CArrayObj* list,const ulong order_ticket); //--- Rückgabe des Flags des Ereignisobjekts in der Ereignisliste bool IsPresentEventInList(CEvent* compared_event);
Die Methode CreateNewEvent(), die ein Handelsereignis in Abhängigkeit vom Auftragsstatus erzeugt, wird in der Methode Refresh()
der Hauptklasse verwendet. Wir werden darauf zurückkommen, wenn wir die Methode Refresh() diskutieren.
Die Methoden zum Empfangen der Listen der verschiedenen Auftragstypen sind recht einfach — die Auswahl durch eine bestimmte Eigenschaft wurde im dritten Teil der Bibliotheksbeschreibung diskutiert. An dieser Stelle werden wir nur kurz erwähnen, dass einige Methoden aus mehreren Iterationen der Selektion nach notwendigen Eigenschaften bestehen.
Die Methode zum Empfangen der Liste der Pending-Orders:
//+------------------------------------------------------------------+ //| Nur Pending-Orders aus der Liste auswählen | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListMarketPendings(CArrayObj* list) { if(list.Type()!=COLLECTION_MARKET_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. List is not a list of market collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL); return list_orders; } //+------------------------------------------------------------------+
Der Typ der an die Methode übergebenen Liste wird zuerst überprüft. Wenn es sich nicht um eine Liste der Marktkollektion handelt, wird die
Fehlermeldung angezeigt und eine leere Liste zurückgegeben.
Anschließend werden Aufträge mit dem Status "Market pending order" aus der an die Methode übergebenen Liste ausgewählt und die erhaltene Liste zurückgegeben.
Die Methoden zum Empfangen von Listen mit entfernten Pending-Orders,
Deals und Schließen
von Aufträgen, die beim Schließen einer Position durch einer entgegengesetzten Position platziert wurden:
//+------------------------------------------------------------------+ //| Auswählen nur der gelöschten Pending-Orders aus der Liste | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListHistoryPendings(CArrayObj* list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); return list_orders; } //+------------------------------------------------------------------+ //| Auswahl nur eines Deals aus der Liste | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListDeals(CArrayObj* list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list_deals; } //+------------------------------------------------------------------+ //| Rückgabe der Liste aller Aufträge CloseBy aus der Liste | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListCloseByOrders(CArrayObj *list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); return list_orders; } //+------------------------------------------------------------------+
Genau wie bei der Rückgabe der Liste der aktiven Pending-Orders:
Der Listentyp wird überprüft und wenn es sich nicht um die historische Kollektion handelt, wird die Nachricht angezeigt und NULL
zurückgegeben.
Dann werden Aufträge mit den Zuständen "Removed pending order" und "Deal" aus der an die Methode übergebenen Liste ausgewählt oder
Aufträge vom Typ ORDER_TYPE_CLOSE_BY je nach Methode ausgewählt und die erhaltene Liste zurückgegeben.
Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Aufträge nach ihrer ID:
//+------------------------------------------------------------------+ //| Liefert die Liste aller Positionsorders nach ihrer ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL); return list_orders; } //+------------------------------------------------------------------+
Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate
Liste aller Objekte, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Als Nächstes werden
alle Deals
aus der erhaltenen Liste entfernt, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der Rückgabe der
Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben hat.
Methode zum Erhalten einer Liste aller Deals, die zu einer Position gehören, nach ihrer ID:
//+------------------------------------------------------------------+ //| Rückgabe der Liste aller Positionsdeals nach ihrer ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsByPosID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Der Listentyp wird zuerst überprüft und wenn es sich nicht um die historische Kollektion handelt, wird die Nachricht angezeigt und NULL
zurückgegeben.
Als Nächstes bilden Sie mit Hilfe der an die Methode übergebenen Liste eine
separate Liste aller Objekte, die einen Zeiger auf die Positions-ID enthalten, die über ihren Parameter an die Methode übergeben
werden.
Danach befinden sich nur
noch Deals in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das Ergebnis der
Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben hat.
Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Markteintrittdeals nach ihrer ID:
//+------------------------------------------------------------------+ //| Rückgabe der Liste aller Markteintrittsdeals (IN) | //| nach der Positions-ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsInByPosID(CArrayObj *list,const ulong position_id) { CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_IN,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate
Liste aller Deals, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Danach befinden sich
nur noch Deals
des Typs DEAL_ENTRY_IN in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das
Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was die Methode im aufrufenden Programm zurückgegeben
hat.
Die Methode zum Erhalten einer Liste aller zu einer Position gehörenden Marktaustrittdeals nach ihrer ID:
//+------------------------------------------------------------------+ //| Rückgabe der Liste aller Marktaustrittsdeals (OUT) | //| nach der Positions-ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsOutByPosID(CArrayObj *list,const ulong position_id) { CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_OUT,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Zuerst bilden wir mit Hilfe der an die Methode übergebenen Liste eine separate
Liste aller Deals, die einen Zeiger auf die Positions-ID enthalten, die durch ihren Parameter an die Methode übergeben werden.
Danach befinden sich
nur noch Deals
des Typs DEAL_ENTRY_OUT in der erhaltenen Liste, und die endgültige Liste wird an das aufrufende Programm zurückgegeben. Das
Ergebnis der Rückgabe der Methode kann NULL sein, daher sollten wir überprüfen, was genau die Methode im aufrufenden Programm
zurückgegeben hat.
Die Methode gibt das Gesamtvolumen alle Deals der Markteintrittspositionen nach deren ID zurück:
//+------------------------------------------------------------------+ //| Rückgabe des Gesamtvolumens aller Deals von IN-Positinen | //| nach der ID | //+------------------------------------------------------------------+ double CEventsCollection::SummaryVolumeDealsInByPosID(CArrayObj *list,const ulong position_id) { double vol=0.0; CArrayObj* list_in=this.GetListAllDealsInByPosID(list,position_id); if(list_in==NULL) return 0; for(int i=0;i<list_in.Total();i++) { COrder* deal=list_in.At(i); if(deal==NULL) continue; vol+=deal.Volume(); } return vol; } //+------------------------------------------------------------------+
Zuerst erhalten wir die Liste aller Markteintrittspositionsdeals, dann summen wir die Volumina aller Deals in einer Schleife. Das resultierende Volumen wird an das aufrufende Programm zurückgegeben. Wenn die an die Methode übergebene Liste leer ist oder es sich nicht um eine historische Kollektionsliste handelt, gibt die Methode Null zurück.
Die Methode gibt das Gesamtvolumen aller Transaktionen einer Marktaustrittsposition nach ihrer ID zurück:
//+--------------------------------------------------------------------------------------+ //| Rückgabe des Gesamtvolumens aller Deals der OUT-Position nach deren | //| ID (Anteil beim Schließen durch eine entgegengesetzte Position wird berücksichtigt) | //+--------------------------------------------------------------------------------------+ double CEventsCollection::SummaryVolumeDealsOutByPosID(CArrayObj *list,const ulong position_id) { double vol=0.0; CArrayObj* list_out=this.GetListAllDealsOutByPosID(list,position_id); if(list_out!=NULL) { for(int i=0;i<list_out.Total();i++) { COrder* deal=list_out.At(i); if(deal==NULL) continue; vol+=deal.Volume(); } } CArrayObj* list_by=this.GetListCloseByOrders(list); if(list_by!=NULL) { for(int i=0;i<list_by.Total();i++) { COrder* order=list_by.At(i); if(order==NULL) continue; if(order.PositionID()==position_id || order.PositionByID()==position_id) { vol+=order.Volume(); } } } return vol; } //+------------------------------------------------------------------+
Wenn ein Teil einer Position, dessen ID an die Methode übergeben wurde, am Schließen einer anderen Position teilgenommen hat (als eine entgegengesetzte), oder ein Teil der Position durch eine entgegengesetzte geschlossen wurde, wird dies in der Position nicht berücksichtigt. Stattdessen wird sie im Eigenschaftsfeld ORDER_PROP_POSITION_BY_ID des letzten Abschlussorder der Position berücksichtigt. Daher hat diese Methode zwei Suchvorgänge nach geschlossenen Volumina — nach Deals und nach Aufträgen zu schließen.
Zuerst erhalten wir die Liste aller Marktaustrittspositionsdeals,
dann
summen wir die Volumina aller Deals in einer Schleife.
Als Nächstes
erhalten wir die Liste aller in der historischen Schließaufträge
der historischen Liste und verwenden eine Schleife, um die Zugehörigkeit
des ausgewählten Auftrags zu der Position zu überprüfen, deren ID der Methode übergeben wurde. Wenn der ausgewählte Auftrag beim
Schließen einer Position beteiligt war, wird
ihr Volumen zum Gesamtvolumen addiert.
Das resultierende
Volumen wird an das aufrufende Programm zurückgegeben. Wenn die an die Methode übergebene Liste leer ist oder es sich nicht um eine
historische Kollektionsliste handelt, gibt die Methode Null zurück.
Die Methode gibt den ersten (Öffnungs-)Positionsauftrag nach ihrer ID zurück:
//+------------------------------------------------------------------+ //| Rückgabe des ersten Auftrags der Liste aller Positionsaufträge | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetFirstOrderFromList(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Zuerst erhalten wir die Liste aller Positionsaufträge. Die erhaltene Liste ist sortiert nach Eröffnungszeit und wir nehmen deren erstes Element. Das wird als erster Positionsauftrag verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.
Die Methode gibt den letzte Positionsauftrag nach dessen ID zurück:
//+------------------------------------------------------------------+ //| Rückgabe des letzten Auftrags der Liste aller Positionsaufträge | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetLastOrderFromList(CArrayObj* list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(list_orders.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Zuerst erhalten wir die Liste aller Positionsaufträge. Die erhaltene Liste ist sortiert nach Eröffnungszeit und wir nehmen deren letztes Element. Das wird als letzter Positionsauftrag verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.
Die Methode gibt den letzten Positionsauftrag nach dessen ID zurück: (ORDER_TYPE_CLOSE_BY Auftragstyp):
//+------------------------------------------------------------------+ //| Rückgabe des letzten Schließauftrags | //| aus der Liste aller Positionsaufträge | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetCloseByOrderFromList(CArrayObj *list,const ulong position_id) { CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list_orders.At(list_orders.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Da beim teilweisen Schließen durch eine entgegengesetzte Position möglich sind und die Volumina der beiden entgegengesetzten
Positionen ungleich sein können, ist der Schließauftrag möglicherweise nicht der einzige der Positionsaufträge. Daher sucht die
Methode nach solchen Aufträgen und gibt den letzte zurück — es ist der letzte Auftrag, der ein Ereignis auslöst.
Zuerst erhalten wir die Liste aller Positionsaufträge. Dann erhalten wir aus der erhaltenen Liste die Liste, die nur Schließaufträge (vom Typ ORDER_TYPE_CLOSE_BY) enthält. Die so erhaltene Liste ist sortiert nach der Eröffnungszeit, und es wird deren letztes Element genommen. Es wird als letzter Schließauftrag der Position verwendet. Der erhaltene Auftrag wird an das aufrufende Programm zurückgegeben. Wenn die Listen leer sind, gibt die Methode NULL zurück.
Wenn wir durch eine entgegengesetzte Position schließen, kann es Situationen geben, in denen die Bibliothek zwei identische Ereignisse
sieht: zwei Positionen sind geschlossen, und nur eine von ihnen hat ist ein Schließauftrag plus zwei Deals. Um also nicht das gleiche
Ereignis in der Kollektion zu duplizieren, sollten wir zunächst das Vorhandensein genau des gleichen Ereignisses in der
Kollektionsliste der Ereignisse überprüfen und, wenn es nicht vorhanden ist, das Ereignis in die Liste aufnehmen.
Die Methode, die einen Auftrag nach dem Ticket zurückgibt:
//+------------------------------------------------------------------+ //| Rückgabe des Auftrags nach dem Ticket | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetOrderByTicket(CArrayObj *list,const ulong order_ticket) { CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,NO_EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TICKET,order_ticket,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Zuerst erstellen wir nur die Liste der Aufträge, dann sortieren
wir die Liste nach dem Ticket, das der Methode übergeben
wurde. Infolgedessen geben wir
entweder NULL (wenn es kein
Auftrag mit einem solchen Ticket gibt) oder die Ticketnummer zurück.
Die Methode, die das in der Liste vorhandene Ereignis zurückgibt, wird verwendet, um zu überprüfen, ob das Ereignis in der Liste enthalten ist:
//+------------------------------------------------------------------+ //| Rückgabe des Flags des Ereignisobjekts in der Ereignisliste | //+------------------------------------------------------------------+ bool CEventsCollection::IsPresentEventInList(CEvent *compared_event) { int total=this.m_list_events.Total(); if(total==0) return false; for(int i=total-1;i>=0;i--) { CEvent* event=this.m_list_events.At(i); if(event==NULL) continue; if(event.IsEqual(compared_event)) return true; } return false; } //+------------------------------------------------------------------+
Der Zeiger auf das zu vergleichende Ereignisobjekt wird der Methode übergeben. Wenn die Kollektionsliste leer ist, wird sofort 'false' zurückgegeben, was bedeutet, dass es kein solches Ereignis in der Liste gibt. Danach wird das nächste Ereignis aus der Liste in einer Schleife genommen und mit dem Ereignis verglichen, das der Methode unter Verwendung der Methode IsEqual() des abstrakten Ereignisses CEvent übergeben wurde. Wenn die Methode 'true' zurückgibt, ist ein solches Ereignisobjekt in der Kollektionsliste der Ereignisse vorhanden. Das Vervollständigen der Schleife oder das Erreichen der letzten Methodenzeichenkette bedeutet, dass es kein Ereignis in der Liste gibt, und es wird 'false' zurückgegeben.
Deklarieren der Methoden im 'public' Teil der Klasse:
public: //--- Auswählen der Ereignisse aus der Kollektion mit einer Zeit im Bereich von begin_time bis end_time. CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Rückgabe des der gesamten Kollektionsliste des Ereignisses "wie besehen" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Aktualisieren der Ereignisliste void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Setzen der Chart-ID des Steuerprogramms void SetChartID(const long id) { this.m_chart_id=id; } //--- Rückgabe des letzten Handelsereignisses auf dem Konto ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Rücksetzen des letzten Handelsereignisses void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Konstructor CEventsCollection(void);
Ich habe die Methoden zum Empfangen der vollständigen Liste, die Listen nach einem Datumsbereich und nach ausgewählten Ganzzahl-, Double- und String-Eigenschaften im dritten Teil der Bibliotheksbeschreibung beschrieben. Hier zeige ich Ihnen nur die Auflistung dieser Methoden, damit Sie sie selbst analysieren können.
Die Methode zur Übernahme der Liste der Ereignisse im angegebenen Datumsbereich:
//+------------------------------------------------------------------+ //| Auswählen der Ereignisse aus der Kollektion nach der Zeit | //| im Bereich von begin_time bis end_time | //+------------------------------------------------------------------+ CArrayObj *CEventsCollection::GetListByTime(const datetime begin_time=0,const datetime end_time=0) { CArrayObj *list=new CArrayObj(); if(list==NULL) { ::Print(DFUN+TextByLanguage("Ошибка создания временного списка","Error creating temporary list")); return NULL; } datetime begin=begin_time,end=(end_time==0 ? END_TIME : end_time); if(begin_time>end_time) begin=0; list.FreeMode(false); ListStorage.Add(list); //--- this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,begin); int index_begin=this.m_list_events.SearchGreatOrEqual(&m_event_instance); if(index_begin==WRONG_VALUE) return list; this.m_event_instance.SetProperty(EVENT_PROP_TIME_EVENT,end); int index_end=this.m_list_events.SearchLessOrEqual(&m_event_instance); if(index_end==WRONG_VALUE) return list; for(int i=index_begin; i<=index_end; i++) list.Add(this.m_list_events.At(i)); return list; } //+------------------------------------------------------------------+
Die Hauptmethode, die aus dem Basis-Bibliotheksobjekt aufgerufen wird, wenn eines der Ereignisse eintritt, ist Refresh().
Derzeit arbeitet die Methode auf Hedge-Konten für MQL5.
Die Methode erhält die Zeiger auf die Listen der Kollektionen von Markt- und historischen Aufträgen, Deals und Positionen sowie die Daten
über die Anzahl der neu erschienenen oder entfernten Aufträge, offenen und geschlossenen Positionen und neuen Deals.
Abhängig von einer geänderten Liste wird die erforderliche Anzahl von Aufträgen oder Deals entsprechend der Anzahl der
Aufträge/Positionen/Deals in einer Schleife ermittelt, und für jeden von ihnen wird die Methode CreateNewEvent() zum Erstellen von
Ereignissen und zum Eintragen in der Kollektionsliste aufgerufen.
Somit wird die neue Methode zur Ereigniserzeugung für jedes eingetretene Ereignis aufgerufen, während das Ereignis in die
Kollektionsliste aufgenommen wird und das aufrufende Programm über alle Ereignisse informiert wird, indem eine nutzerdefinierte
Nachricht an das Diagramm des aufrufenden Programms gesendet wird.
Die Variable m_trade_event der Klasse erhält den Wert des zuletzt aufgetretenen Ereignisses. Die 'public' Methode GetLastTradeEvent()
gibt den Wert des letzten Handelsgeschehens zurück. Es gibt auch die Methode zum Zurücksetzen des letzten Handelsereignisses (ähnlich
wie bei GetLastError() und ResetLastError()).
Darüber hinaus gibt es Methoden, die die Kollektionslisten der Ereignisse sowohl vollständig, nach einem Zeitbereich und bestimmten
Kriterien zurückgeben. Das aufrufende Programm weiß immer, dass ein Ereignis oder mehrere Ereignisse aufgetreten sind, und es ist
möglich, die Liste aller dieser Ereignisse in einer erforderlichen Menge anzufordern und sie gemäß der Logik des eingebauten Programms zu
behandeln.
Betrachten wir den Code der Methoden Refresh() und CreateNewEvent().
Die Methode zum Aktualisieren der Kollektionsliste der Ereignisse:
//+------------------------------------------------------------------+ //| Aktualisieren der Ereignisliste | //+------------------------------------------------------------------+ void CEventsCollection::Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals) { //--- Rückkehren, wenn die Liste leer ist if(list_history==NULL || list_market==NULL) return; //--- Im Falle eines Hedging-Kontos if(this.m_is_hedge) { //--- Wenn das Ereignis in der Umgebung des Marktes existiert if(is_market_event) { //--- wenn sich die Anzahl der platzierten Pending-Orders erhöht hat if(new_market_pendings>0) { //--- Empfangen der Liste der neuesten, platzierten Pending-Orders CArrayObj* list=this.GetListMarketPendings(list_market); if(list!=NULL) { //--- Sortieren der neuen Liste nach der Platzierungszeit der Orders list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); //--- Nehmen der Order-Anzahl, die gleich der Anzahl neu platzierten vom Ende der Liste ist, in einer Schleife (die letzten N Ereignisse) int total=list.Total(), n=new_market_pendings; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Erhalt der Order von der Liste, wenn es eine Pending-Order ist, wird das Handelsereignis gesetzt COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_MARKET_PENDING) this.CreateNewEvent(order,list_history,list_market); } } } } //--- Wenn das Ereignis in Kontohistorie existiert if(is_history_event) { //--- Wenn sich die Anzahl historischer Aufträge erhöht hat if(new_history_orders>0) { //--- Erhalt der Liste nur der entfernten Pending-Orders CArrayObj* list=this.GetListHistoryPendings(list_history); if(list!=NULL) { //--- Sortieren der Liste nach der Entfernungszeit der Orders list.Sort(SORT_BY_ORDER_TIME_CLOSE_MSC); //--- Nehmen der Order-Anzahl gleich der Anzahl neu entfernten vom Ende der Liste in einer Schleife (die letzten N Ereignisse) int total=list.Total(), n=new_history_orders; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Empfangen einer Order von der Liste. Wenn das eine entfernte Pending-Order ist, wird das Handelsereignis gesetzt COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_HISTORY_PENDING) this.CreateNewEvent(order,list_history,list_market); } } } /--- Wenn sich die Anzahl der Deals sich erhöht hat if(new_deals>0) { //--- Empfangen der Liste nur der Deals CArrayObj* list=this.GetListDeals(list_history); if(list!=NULL) { //--- Sortieren der neuen Liste nach der Dealzeit list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); //--- Nehmen der Anzahl der Deals, die gleich der Anzahl der neuen vom Ende der Liste ist, in einer Schleife (die letzten N Ereignisse) int total=list.Total(), n=new_deals; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Erhalt eines Deals von der Liste und setzen des Handelsereignisses COrder* order=list.At(i); if(order!=NULL) this.CreateNewEvent(order,list_history,list_market); } } } } } //--- Im Falle eines Netting-Kontos else { } } //+------------------------------------------------------------------+
Der einfache Code der Methode enthält alle notwendigen Bedingungen und Aktionen, wenn diese Bedingungen erfüllt sind. Ich glaube, hier ist
alles ziemlich klar. Derzeit werden Ereignisse eines Hedging-Kontos behandelt.
Betrachten wir die Methode zum Erstellen eines neuen Ereignisses:
//+------------------------------------------------------------------+ //| Erstellen eines Handelsereignisses abhängig vom Auftragsstatus | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market) { int trade_event_code=TRADE_EVENT_FLAG_NO_EVENT; ENUM_ORDER_STATUS status=order.Status(); //--- Pending-Order platziert if(status==ORDER_STATUS_MARKET_PENDING) { trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; CEvent* event=new CEventOrderPlased(trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Zeit des Ereignisses event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Dealtyp des Ereignisses event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Auftragsticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Auftragstyp des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Auftragstyp des Ereignisses event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Auftragsticket des Ereignisses event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Auftragsticket event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID der entgegengesetzten Position event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer des Auftrags event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Zeit des Auftrags event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Preis, bei dem ein Ereignis auftritt event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Preis zu dem ein Auftrag platziert wurde event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Schließpreis des Auftrags event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss des Auaftrags event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit des Auftrags event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Verlangtes Volumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Ausgeführtes Volumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Verbliebenes (nicht ausgeführtes) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Auftragssymbol //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Pending-Order entfernt if(status==ORDER_STATUS_HISTORY_PENDING) { trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED; CEvent* event=new CEventOrderRemoved(trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL : order.State()==ORDER_STATE_EXPIRED ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC()); // Ereigniszeit event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Ereignisgrund (aus ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Ereignis der Auftragstyp event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ereignis Auftragsticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Auftragstyp, der ein Deal-Ereignis Type auslöst (der erste Auftrag zur Position) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer des Auftrags event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Eröffnungspreis des Auftrags event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Schließpreis des Auftrags event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss Auftragspreis event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit Auftragspreis event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Verlangtes Volumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()-order.VolumeCurrent()); // Ausgeführtes Volumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Verbliebenes (nicht ausgeführtes) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen des Ereignis-Objekts, falls es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Position eröffnet (__MQL4__) if(status==ORDER_STATUS_MARKET_POSITION) { trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; CEvent* event=new CEventPositionOpen(trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen()); // Ereigniszeit event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Deal-Typ des Ereignisses event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Deal-Ticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer von Order/Deal/Position event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Eröffnungspreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Schließpreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss der Position event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit der Position event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Verlangtes Volumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Ausgeführtes Volumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Verbliebenes (nicht ausgeführtes) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen des Ereignis-Objekts, falls es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Neuer Deal (__MQL5__) if(status==ORDER_STATUS_DEAL) { //--- Neue Saldenoperationen if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL) { trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE; CEvent* event=new CEventBalanceOperation(trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) : (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT) ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Ereigniszeit event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Deal-Typ des Ereignisses event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Auftragsticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer von Order/Deal/Position event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Eröffnungspreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Schließpreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Dealpreis des StopLoss' event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Dealpreis des TakeProfits event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order.Volume()); // Verlangtes Volumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Ausgeführtes Volumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order.VolumeCurrent()); // Verbliebenes (nicht ausgeführtes) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses this.m_list_events.InsertSort(event); event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Wenn es keine Saldenoperation ist else { //--- Markteintritt if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int reason=EVENT_REASON_DONE; //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und berechnen des Gesamtvolumens double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge ulong order_ticket=order.GetProperty(ORDER_PROP_DEAL_ORDER); COrder* order_first=this.GetOrderByTicket(list_history,order_ticket); COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID()); //--- Wenn es keinen letzten Auftrag gibt, müssen erster und letzter Positionsauftrag übereinstimmen if(order_last==NULL) order_last=order_first; if(order_first!=NULL) { //--- Wenn das Auftragsvolumen teilweise eröffnet wurde, ist das eine teilweise Ausführung if(this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume()) { trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_DONE_PARTIALLY; } //--- Wenn der Eröffnungsauftrag eine Pending-Order ist, wurde die Pending-Order ausgelöst if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY) { trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- Wenn ein Auftrag teilweise ausgeführt wurde, wird die teilweise Auftragsausführung als Grund des Ereignisses gesetzt reason= (this.SummaryVolumeDealsInByPosID(list_history,order.PositionID())<order_first.Volume() ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } CEvent* event=new CEventPositionOpen(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Ereigniszeit (Eröffnungszeit der Position) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Ereignis-Typ des Deals event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Deal-Ticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer von Order/Deal/Position event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis (Eröffnungspreis der Position) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Eröffnungspreis des Auftrags (erster Auftrag des Eröffnungspreises der Position) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Schließpreis des Auftrags (letzter Auftrag des Schließpreises der Position) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss (StopLoss des Positionsauftrags) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit (TakeProfit des Positionsauftrags) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,order_first.Volume()); // Verlangtes Volumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,volume_in); // Ausgeführtes Volumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,order_first.Volume()-volume_in); // Verbliebenes (nicht ausgeführtes) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen eines Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Marktaustritt else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID()); COrder* order_last=this.GetLastOrderFromList(list_history,order.PositionID()); if(order_first!=NULL && order_last!=NULL) { //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID()); //--- Berechnen des aktuellen Volumens der geschlossenen Position int dgl=(int)DigitsLots(order.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es eine teilweise Ausführung if(volume_current>0) { trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- Wenn ein Schließauftrag teilweise ausgeführt wurde, wird die teilweise Auftragsausführung als Grund des Ereignisses if(order_last.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- Wenn das Schließ-Flag auf StopLoss für das Schließen der Position gesetzt wurde, dann wurde StopLoss ausgeführt //--- Wenn ein StopLoss teilweise ausgeführt wurde, wird die teilweise StopLoss-Ausführung als Grund des Ereignisses gesetzt if(order_last.IsCloseByStopLoss()) { trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- Wenn das Schließ-Flag auf TakeProfit für das Schließen der Position gesetzt wurde, dann wurde TakeProfit ausgeführt //--- Wenn ein TakeProfit teilweise ausgeführt wurde, wird die teilweise TakeProfit-Ausführung als Grund des Ereignisses gesetzt else if(order_last.IsCloseByTakeProfit()) { trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Ereigniszeit (Schließzeit der Positioon) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Ereignis-Typ des Deals event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Deal-Ticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket eines Auftrags auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer von Order/Deal/Position event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis (Schließpreis die Position) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Eröffnungspreis des Auftrags (erster Auftrag des Eröffnungspreises der Position) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Schließpreis des Auftrags (letzter Auftrag des Schließpreises der Position) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss (StopLoss des Positionsauftrags) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit (TakeProfit des Positionsauftrags) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,volume_in); // Anfangsvolumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Schließvolumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_in-volume_out); // Verbliebenes (aktuelles) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen eines Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzten des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn das Ereignis bereits in der Liste steht, entfernen des neuen Ereignisobjekts und der Anzeige der Debugging-Nachricht else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- entgegengesetzte Position else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY) { trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE_BY_POS; //--- Nehmen des ersten und letzten Positionsauftrags aus der Liste aller Positionsaufträge COrder* order_first=this.GetFirstOrderFromList(list_history,order.PositionID()); COrder* order_close=this.GetCloseByOrderFromList(list_history,order.PositionID()); if(order_first!=NULL && order_close!=NULL) { //--- Hinzufügen des Flags beim Schließen durch eine Gegenposition trade_event_code+=TRADE_EVENT_FLAG_BY_POS; //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens double volume_in=this.SummaryVolumeDealsInByPosID(list_history,order.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,order.PositionID());//+order_close.Volume(); //--- Berechnen des aktuellen Volumens der geschlossenen Position int dgl=(int)DigitsLots(order.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Suche nach allen Positionsdeals in Richtung der Eröffnung und Schließens und berechnen des Gesamtvolumens double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID()); double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID());//+order_close.Volume(); //--- Berechnen des aktuellen Volumens der geschlossenen Position double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl); //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es ein teilweises Schließen if(volume_current>0 || order_close.VolumeCurrent()>0) { //--- Hinzufügen des Flags eines teilweisen Schließens trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- Wenn das Auftragsvolumen teilweise geschlossen wurde, ist es ein teilweises Schließen durch das Volumen einer entgegengesetzten Position reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS); } //--- Wenn das Auftragsvolumen vollständig geschlossen wurde, und es gibt ein teilweises Ausführen durch eine entgegengesetzte, gibt es ein teilweises Schließen mit dem Volumen der entgegengesetzten Position else { if(volume_opp_current>0) { reason=EVENT_REASON_DONE_BY_POS_PARTIALLY; } } CEvent* event=new CEventPositionClose(trade_event_code,order.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Ereigniszeit event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Grund des Ereignisses (aus der Enumeration ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Ereignis-Typ des Deals event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Deal-Ticket des Ereignisses event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Ereignisses (der letzte Positionsauftrag) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Auftragszeit auf Basis des dadurch geöffneten Deal-Ereignisses (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Auftragstyp auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Auftragsticket auf Basis des dadurch geöffneten Deal-Position (der erste Positionsauftrag) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID()); // ID der entgegengesetzten Positions event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Magicnummer von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Ereignispreis event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Eröffnungspreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Schließpreis von Order/Deal/Position event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss (StopLoss des Positionsauftrags) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit (TakeProfit des Positionsauftrags) event.SetProperty(EVENT_PROP_VOLUME_INITIAL,::NormalizeDouble(volume_in,dgl));// Anfangsvolumen event.SetProperty(EVENT_PROP_VOLUME_EXECUTED,order.Volume()); // Schließvolumen event.SetProperty(EVENT_PROP_VOLUME_CURRENT,volume_current); // Verbliebenes (aktuelles) Volumen event.SetProperty(EVENT_PROP_PROFIT,order.ProfitFull()); // Gewinn event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Umkehrung else if(order.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT) { //--- Positionsumkehrung Print(DFUN,"Position reversal"); order.Print(); } } } } //+------------------------------------------------------------------+
Die Methode erweist sich als recht lang. Daher werden alle Beschreibungen der notwendigen Prüfungen und entsprechenden Aktionen direkt im Code angegeben.
Die Methode überprüft den Status eines übergebenen Auftrags und alle notwendigen Komponenten eines aufgetretenen Ereignisses in Abhängigkeit von seiner Art (platzierte Pending-Order, entfernte Pending-Order, Deals). Ein neues Ereignis wird angelegt und mit Daten gefüllt, die dem Auftrag und dem Ereignistyp entsprechen, während das Ereignis in die Ereigniskollektion aufgenommen wird, und schließlich wird eine Nachricht über dieses Ereignis an die Regelprogrammkarte gesendet und die Variable, die den Typ des zuletzt aufgetretenen Ereignisses speichert, ausgefüllt.
Die Kollektion der Ereignisse ist fertig. Nun müssen wir sie in das Basisobjekt der Bibliothek einbinden.
Nach dem Erstellen der Klasse der Ereigniskollektion sind einige Dinge, die wir im vierten Teil der Basisobjektklasse CEngine zur Verfolgung von Events gemacht haben, redundant, daher sollte das Basisobjekt überarbeitet werden.
- Entfernen wir die 'private' Variable m_trade_event_code der Klasse, die den Statuscode des Trading-Events speichert.
- Entfernen wir die 'private' Methoden:
- SetTradeEvent() zum Dekodieren eines Ereigniscodes,
- IsTradeEventFlag() zur Rückgabe des Vorhandenseins eines Flags in einem Handelsereignis,
- WorkWithHedgeCollections() und WorkWithNettoCollections() zum Arbeiten mit Hedging- und Netting-Kollektionen und
- TradeEventCode() zur Rückgabe des Codes eines Handelsereignisses
Fügen wir das Einbinden der Klassendatei der Kollektion der
Handelsereignisse dem Klassenkörper hinzu, deklarieren das Objekt der
Ereigniskollektion, fügen die Methode TradeEventsControl() zum
Arbeiten mit Ereignissen in den 'private' Teil der Klasse ein, ändern den Methodennamen GetListHistoryDeals() auf GetListDeals()
im 'public' Abschnitt. Deals befinden sich immer in der historischen Kollektion, so dass ich glaube, es besteht keine Notwendigkeit, die
Kollektion explizit im Methodennamen zu erwähnen. Ändern wir die Implementierung der Methode zum Zurücksetzen des letzten
Handelsereignisses: Da wir nun das letzte Ereignis von der Event-Sammelklasse erhalten und die Methode zum Zurücksetzen des letzten
Ereignisses innerhalb der Klasse vorhanden ist, müssen wir nur die gleichnamige Methode aus der Ereigniskollektion der Methode
ResetLastTradeEvent() der Klasse aufrufen.
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Services\TimerCounter.mqh" //+------------------------------------------------------------------+ //| Bibliothek der Basisklasse | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Kollektion der historischen Aufträge und Deals CMarketCollection m_market; // Kollektion der Marktorder und Deals CEventsCollection m_events; // Kollektion der Ereignisse CArrayObj m_list_counters; // Liste der Timerzähler bool m_first_start; // Flag des Erststarts bool m_is_hedge; // Flag des Hedging-Kontos bool m_is_market_trade_event; // Flag eines Handelsereignisses des Kontos bool m_is_history_trade_event; // Flag eines historischen Handelsereignisses auf dem Konto ENUM_TRADE_EVENT m_acc_trade_event; // Handelsereignis auf dem Konto //--- Rückgabe des Zählerindex über die ID int CounterIndex(const int id) const; //--- Rückgabe (1) des Flags des Erststarts, (2) der Existenz des Flags eines Handelsereignisses bool IsFirstStart(void); //--- Arbeiten mit den Ereignissen void TradeEventsControl(void); //--- Rückgabe der letzten (1) Pending-Order, (2) Marktorder, (3) letzten Position, (4) Position nach Ticket COrder* GetLastMarketPending(void); COrder* GetLastMarketOrder(void); COrder* GetLastPosition(void); COrder* GetPosition(const ulong ticket); //--- Rückgabe der letzten (1) gelöschten Pending-Order, (2) historischen Marktorder, (3) historischen Aufträge (Markt- oder Pending-Orders) nach Ticket COrder* GetLastHistoryPending(void); COrder* GetLastHistoryOrder(void); COrder* GetHistoryOrder(const ulong ticket); //--- Rückgabe des (1) ersten und (2) des letzten historischen Marktorder aus der Liste aller Positionen, (3) des letzten Deals COrder* GetFirstOrderPosition(const ulong position_id); COrder* GetLastOrderPosition(const ulong position_id); COrder* GetLastDeal(void); public: //--- Rückgabe der Liste aller (1) Positionen, (2) Pending-Order und (3) Marktorders CArrayObj* GetListMarketPosition(void); CArrayObj* GetListMarketPendings(void); CArrayObj* GetListMarketOrders(void); //--- Rückgabe der Liste aller historischen (1) Aufträge, (2) gelöschten Pending-Orders, (3) Deals, (4) Positionen nach deren ID CArrayObj* GetListHistoryOrders(void); CArrayObj* GetListHistoryPendings(void); CArrayObj* GetListDeals(void); CArrayObj* GetListAllOrdersByPosID(const ulong position_id); //--- Rücksetzen des letzten Handelsereignisses void ResetLastTradeEvent(void) { this.m_events.ResetLastTradeEvent(); } //--- Rückgabe des (1) Handelsereignisses und (2) Flag des Hedging-Kontos ENUM_TRADE_EVENT LastTradeEvent(void) const { return this.m_acc_trade_event; } bool IsHedge(void) const { return this.m_is_hedge; } //--- Erstellen des Timerzählers void CreateCounter(const int id,const ulong frequency,const ulong pause); //--- Timer void OnTimer(void); //--- Constructor/Destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Fügen wir im Konstruktor der Klasse CEngine die Behandlung des Millisekunden-Timer-Entwicklungsergebnisses hinzu. Wenn er nicht angelegt
ist, zeigen wir eine entsprechende Meldung im Journal an. Als Nächstes werden wir die Klasse für die Behandlung bestimmter Fehler
entwickeln, Flags setzen, die einem bibliotheksbasierten Programm sichtbar sind, und Fehlersituationen verarbeiten.
//+------------------------------------------------------------------+ //| CEngine Konstruktor | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT) { ::ResetLastError(); if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError()); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE); this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); } //+------------------------------------------------------------------+
Rufen wir im Timer der Klasse die Methode TradeEventsControl()
auf, nachdem der Timer der Auftrags-, Deal- und Positionskollektion nicht unterbrochen wurde.
//+------------------------------------------------------------------+ //| CEngine Timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer der Kollektion der historischen Aufträge, Deals Marktorders und Positionen int index=this.CounterIndex(COLLECTION_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); //--- Wenn nicht 'in Pause', arbeite mit der Kollektion der Ereignisse if(counter!=NULL && counter.IsTimeDone()) { this.TradeEventsControl(); } } } //+------------------------------------------------------------------+
Verbessern wir die Methode, die einen historischen Auftrag über das Ticket zurückgibt. Da die historische Kollektionsliste Pending-Orders, aktivierte Marktorders und Aufträge, die als Schließaufträge fungieren, beim Schließen durch eine entgegengesetzte Position enthalten kann, müssen wir alle Auftragstypen berücksichtigen.
Suchen wir dazu zunächst in der Liste der Markt- und Schließaufträge nach einem
Auftrag mit dem Ticket. Wenn die Liste leer ist, suchen wir nach einer entfernten Pending-Order mit dem gleichen Ticket. Wenn auch
diese Liste den Auftrag nicht enthält, wird NULL zurückgegeben. Andernfalls gibt das Programm das erste Element der sortierten Liste
zurück. Wenn in der Liste kein Auftrag gefunden wurde, wird NULL zurückgegeben.
//+------------------------------------------------------------------+ //| Rückgabe historischer Aufträge nach dem Ticket | //+------------------------------------------------------------------+ COrder* CEngine::GetHistoryOrder(const ulong ticket) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL || list.Total()==0) { list=this.GetListHistoryPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL) return NULL; } COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Implementieren wir die Methode TradeEventsControl() für die Arbeit mit Ereignissen auf dem Konto:
//+------------------------------------------------------------------+ //| Prüfen der Handelsereignisse | //+------------------------------------------------------------------+ void CEngine::TradeEventsControl(void) { //--- Initialisieren der Codes und Flags der Handelsereignisse this.m_is_market_trade_event=false; this.m_is_history_trade_event=false; //--- Aktualisieren der Liste this.m_market.Refresh(); this.m_history.Refresh(); //--- Aktionen beim ersten Start if(this.IsFirstStart()) { this.m_acc_trade_event=TRADE_EVENT_NO_EVENT; return; } //--- Prüfen der Änderungen des Marktzustands und der Kontohistorie this.m_is_market_trade_event=this.m_market.IsTradeEvent(); this.m_is_history_trade_event=this.m_history.IsTradeEvent(); //--- Im Falle irgendeines Ereignisses, werden die Listen, Flags und die Anzahl der neuen Aufträge und Deals an die Kollektion der Ereignisse gesendet und aktualisiert if(this.m_is_history_trade_event || this.m_is_market_trade_event) { this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(), this.m_is_history_trade_event,this.m_is_market_trade_event, this.m_history.NewOrders(),this.m_market.NewPendingOrders(), this.m_market.NewMarketOrders(),this.m_history.NewDeals()); //--- Abrufen des letzten Handelsereignisses auf dem Konto this.m_acc_trade_event=this.m_events.GetLastTradeEvent(); } }
Diese Methode ist viel kürzer als ihr Vorgänger WorkWithHedgeCollections() aus dem vierten Teil der Bibliotheksbeschreibung.
Die Methode ist einfach und bedarf keiner Erklärungen. Der Code enthält alle Kommentare, so dass Sie seine einfache Logik verstehen können.
Hier ist der vollständige Code der aktualisierten Klasse CEngine:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Services\TimerCounter.mqh" //+------------------------------------------------------------------+ //| Bibliothek der Basisklasse | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Kollektion der historischen Aufträge und Deals CMarketCollection m_market; // Kollektion der Marktorder und Deals CEventsCollection m_events; // Kollektion der Ereignisse CArrayObj m_list_counters; // Liste der Timerzähler bool m_first_start; // Flag des Erststarts bool m_is_hedge; // Flag des Hedging-Kontos bool m_is_market_trade_event; // Flag eines Handelsereignisses des Kontos bool m_is_history_trade_event; // Flag eines historischen Handelsereignisses auf dem Konto ENUM_TRADE_EVENT m_acc_trade_event; // Handelsereignis auf dem Konto //--- Rückgabe des Zählerindex über die ID int CounterIndex(const int id) const; //--- Rückgabe (1) des Flags des Erststarts, (2) der Existenz des Flags eines Handelsereignisses bool IsFirstStart(void); //--- Arbeiten mit den Ereignissen void TradeEventsControl(void); //--- Rückgabe der letzten (1) Pending-Order, (2) Marktorder, (3) letzten Position, (4) Position nach Ticket COrder* GetLastMarketPending(void); COrder* GetLastMarketOrder(void); COrder* GetLastPosition(void); COrder* GetPosition(const ulong ticket); //--- Rückgabe der letzten (1) gelöschten Pending-Order, (2) historischen Marktorder, (3) historischen Aufträge (Markt- oder Pending-Orders) nach Ticket COrder* GetLastHistoryPending(void); COrder* GetLastHistoryOrder(void); COrder* GetHistoryOrder(const ulong ticket); //--- Rückgabe des (1) ersten und (2) des letzten historischen Marktorder aus der Liste aller Positionen, (3) des letzten Deals COrder* GetFirstOrderPosition(const ulong position_id); COrder* GetLastOrderPosition(const ulong position_id); COrder* GetLastDeal(void); public: //--- Rückgabe der Liste aller (1) Positionen, (2) Pending-Order und (3) Marktorders CArrayObj* GetListMarketPosition(void); CArrayObj* GetListMarketPendings(void); CArrayObj* GetListMarketOrders(void); //--- Rückgabe der Liste aller historischen (1) Aufträge, (2) gelöschten Pending-Orders, (3) Deals, (4) Positionen nach deren ID CArrayObj* GetListHistoryOrders(void); CArrayObj* GetListHistoryPendings(void); CArrayObj* GetListDeals(void); CArrayObj* GetListAllOrdersByPosID(const ulong position_id); //--- Rücksetzen des letzten Handelsereignisses void ResetLastTradeEvent(void) { this.m_events.ResetLastTradeEvent(); } //--- Rückgabe des (1) Handelsereignisses und (2) Flag des Hedging-Kontos ENUM_TRADE_EVENT LastTradeEvent(void) const { return this.m_acc_trade_event; } bool IsHedge(void) const { return this.m_is_hedge; } //--- Erstellen des Timerzählers void CreateCounter(const int id,const ulong frequency,const ulong pause); //--- Timer void OnTimer(void); //--- Constructor/Destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+ //| CEngine Konstruktor | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT) { ::ResetLastError(); if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError()); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE); this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); } //+------------------------------------------------------------------+ //| CEngine Destruktor | //+------------------------------------------------------------------+ CEngine::~CEngine() { ::EventKillTimer(); } //+------------------------------------------------------------------+ //| CEngine Timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer der Kollektion der historischen Aufträge, Deals Marktorders und Positionen int index=this.CounterIndex(COLLECTION_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); //--- Wenn nicht 'in Pause', arbeite mit der Kollektion der Ereignisse if(counter!=NULL && counter.IsTimeDone()) { this.TradeEventsControl(); } } } //+------------------------------------------------------------------+ //| Erstellen des Timerzählers | //+------------------------------------------------------------------+ void CEngine::CreateCounter(const int id,const ulong step,const ulong pause) { if(this.CounterIndex(id)>WRONG_VALUE) { ::Print(TextByLanguage("Ошибка. Уже создан счётчик с идентификатором ","Error. Already created counter with id "),(string)id); return; } m_list_counters.Sort(); CTimerCounter* counter=new CTimerCounter(id); if(counter==NULL) ::Print(TextByLanguage("Не удалось создать счётчик таймера ","Failed to create timer counter "),(string)id); counter.SetParams(step,pause); if(this.m_list_counters.Search(counter)==WRONG_VALUE) this.m_list_counters.Add(counter); else { string t1=TextByLanguage("Ошибка. Счётчик с идентификатором ","Error. Counter with ID ")+(string)id; string t2=TextByLanguage(", шагом ",", step ")+(string)step; string t3=TextByLanguage(" и паузой "," and pause ")+(string)pause; ::Print(t1+t2+t3+TextByLanguage(" уже существует"," already exists")); delete counter; } } //+------------------------------------------------------------------+ //| Rückgabe des Zählerindex der Liste über die ID | //+------------------------------------------------------------------+ int CEngine::CounterIndex(const int id) const { int total=this.m_list_counters.Total(); for(int i=0;i<total;i++) { CTimerCounter* counter=this.m_list_counters.At(i); if(counter==NULL) continue; if(counter.Type()==id) return i; } return WRONG_VALUE; } //+------------------------------------------------------------------+ //| Rückgabe des Flags des ersten Starts, rücksetzen des Flags | //+------------------------------------------------------------------+ bool CEngine::IsFirstStart(void) { if(this.m_first_start) { this.m_first_start=false; return true; } return false; } //+------------------------------------------------------------------+ //| Prüfen der Handelsereignisse | //+------------------------------------------------------------------+ void CEngine::TradeEventsControl(void) { //--- Initialisieren des Codes und der Flags des Handelsereignisses this.m_is_market_trade_event=false; this.m_is_history_trade_event=false; //--- Aktualisieren der Liste this.m_market.Refresh(); this.m_history.Refresh(); //--- Aktionen beim ersten Start if(this.IsFirstStart()) { this.m_acc_trade_event=TRADE_EVENT_NO_EVENT; return; } //--- Prüfen der Änderungen des Marktstatus' und der Kontohistorie this.m_is_market_trade_event=this.m_market.IsTradeEvent(); this.m_is_history_trade_event=this.m_history.IsTradeEvent(); //--- Im Falle irgendeines Ereignisses, werden die Listen, Flags und die Anzahl der neuen Aufträge und Deals an die Kollektion der Ereignisse gesendet und aktualisiert if(this.m_is_history_trade_event || this.m_is_market_trade_event) { this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(), this.m_is_history_trade_event,this.m_is_market_trade_event, this.m_history.NewOrders(),this.m_market.NewPendingOrders(), this.m_market.NewMarketOrders(),this.m_history.NewDeals()); //--- Abrufen des letzten Handelsereignisses auf dem Konto this.m_acc_trade_event=this.m_events.GetLastTradeEvent(); } } //+------------------------------------------------------------------+ //| Rückgabe der Liste der Marktpositionen | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketPosition(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste der Pending-Orders | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketPendings(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste der Marktordersd | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListMarketOrders(void) { CArrayObj* list=this.m_market.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_ORDER,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste der historischen Aufträge | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListHistoryOrders(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_ORDER,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste der entfernten Pending-Orders | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListHistoryPendings(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste der Deals | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListDeals(void) { CArrayObj* list=this.m_history.GetList(); list=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der Liste aller Positionen | //+------------------------------------------------------------------+ CArrayObj* CEngine::GetListAllOrdersByPosID(const ulong position_id) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_ID,position_id,EQUAL); return list; } //+------------------------------------------------------------------+ //| Rückgabe der letzten Position | //+------------------------------------------------------------------+ COrder* CEngine::GetLastPosition(void) { CArrayObj* list=this.GetListMarketPosition(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der Position nach der Ticketnummer | //+------------------------------------------------------------------+ COrder* CEngine::GetPosition(const ulong ticket) { CArrayObj* list=this.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TICKET); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe des letzten Deals | //+------------------------------------------------------------------+ COrder* CEngine::GetLastDeal(void) { CArrayObj* list=this.GetListDeals(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der letzten Pending-Order im Markt | //+------------------------------------------------------------------+ COrder* CEngine::GetLastMarketPending(void) { CArrayObj* list=this.GetListMarketPendings(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der letzten historischen Pending-Order | //+------------------------------------------------------------------+ COrder* CEngine::GetLastHistoryPending(void) { CArrayObj* list=this.GetListHistoryPendings(); if(list==NULL) return NULL; list.Sort(#ifdef __MQL5__ SORT_BY_ORDER_TIME_OPEN_MSC #else SORT_BY_ORDER_TIME_CLOSE_MSC #endif); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der letzten Marktorder | //+------------------------------------------------------------------+ COrder* CEngine::GetLastMarketOrder(void) { CArrayObj* list=this.GetListMarketOrders(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der letzten historischen Marktorder | //+------------------------------------------------------------------+ COrder* CEngine::GetLastHistoryOrder(void) { CArrayObj* list=this.GetListHistoryOrders(); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe historischer Aufträge nach dem Ticket | //+------------------------------------------------------------------+ COrder* CEngine::GetHistoryOrder(const ulong ticket) { CArrayObj* list=this.GetListHistoryOrders(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL || list.Total()==0) { list=this.GetListHistoryPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,(long)ticket,EQUAL); if(list==NULL) return NULL; } COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der ersten historischen Marktorder | //| aus der Liste aller Positionen | //+------------------------------------------------------------------+ COrder* CEngine::GetFirstOrderPosition(const ulong position_id) { CArrayObj* list=this.GetListAllOrdersByPosID(position_id); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN); COrder* order=list.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+ //| Rückgabe der letzten historischen Marktorder | //| aus der Liste aller Positionen | //+------------------------------------------------------------------+ COrder* CEngine::GetLastOrderPosition(const ulong position_id) { CArrayObj* list=this.GetListAllOrdersByPosID(position_id); if(list==NULL) return NULL; list.Sort(SORT_BY_ORDER_TIME_OPEN); COrder* order=list.At(list.Total()-1); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Testen der Prozesse zum Definieren, Behandeln und Empfangen von Ereignissen
Jetzt können wir mit Ereignissen arbeiten. Es ist an der Zeit, den EA auf die Prüfung und Behandlung von Ereignisbeschreibungen vorzubereiten und diese dem Steuerungsprogramm zu übermitteln.
Erstellen wir im Terminalverzeichnis\MQL5\Experts\TestDoEasy den Ordner Part05 und kopieren den EA TestDoEasyPart04.mq5 des vorherigen Teils unter einem neuen Namen: TestDoEasyPart05.mq5.
Nun ändern wir die Ereignisbehandlung OnChartEvent(), um nutzerdefinierte Ereignisse zu empfangen:
//+------------------------------------------------------------------+ //| Funktion der Chart-Events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+
Hier, wenn die Ereignis-ID die nutzerdefinierte Ereignis-ID
überschreitet oder gleich ist,
empfangen wir den Ereigniscode, der von der abgeleiteten Klasse
CEvent aus der Bibliothek übergeben wird. Beim Senden eines nutzerdefinierten Ereignisses über die Funktion
EventChartCustom() mit dem Funktionsparameter custom_event_id
(derjenige, der unser Ereignis erhält), wird der Wert der Konstante CHARTEVENT_CUSTOM (gleich 1000) aus der Enumeration ENUM_CHART_EVENT
zum Ereigniswert addiert. Um den Ereigniswert zurückzubekommen, müssen wir daher einfach den Wert CHARTEVENT_CUSTOM von der Ereignis-ID
abziehen. Danach zeigen wir die Ereignisdaten im Journal des Terminal an.
Folgende Daten werden angezeigt: die ID ('wie besehen'), die Ereignisbeschreibung in Form des Wertes der Enumeration
ENUM_TRADE_EVENT, der lparam-Wert, der den Auftrag oder das Positionsticket speichert, der dparam-Wert, der den Auftragspreis
speichert, und der Sparam-Wert — dem Symbol eines Auftrags oder einer am Event teilnehmenden Position oder der Name der Kontowährung,
falls es sich um eine Saldenoperation handelt.
Zum Beispiel:
2019.04.06 03:19:54.442 OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD
Außerdem müssen wir die für den Teilabschluss berechnete Losgröße korrigieren. In den Vorgängerversionen der Test-EAs war das falsch, da der Wert des nicht ausgeführten Positionsvolumens (VolumeCurrent()) für die Losgrößenberechnung verwendet wurde. Er ist beim Öffnen einer Position im Tester immer gleich Null, da der Tester keine Teilöffnungen simuliert. Dementsprechend wurde der minimale Wert der Losgröße zum Schließen herangezogen, da die Funktion für die Berechnung der Losgröße Null immer auf den kleinsten erlaubten Wert eingestellt hat.
Finden wir die Zeichenketten, in denen die Losgröße für den Teilabschluss berechnet wird und VolumeCurrent() durch Volume() ersetzen:
//--- Berechnen des Schließvolumens und schließen der Hälfte der Kaufposition nach dem Ticket. trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); //--- Berechnen des Schließvolumens und schließen der Hälfte der Verkaufsposition nach dem Ticket. trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
Nur zwei Stellen im Code — Schließen der Hälfte der Kaufposition und Schließen der Hälfte der Verkaufsposition.
Außerdem fügen wir Tastenverschiebung um die X- und Y-Achse zu den EA-Eingaben hinzu, um die Position der Schaltflächen auf dem Chart des visuellen Tester zu verbessern (ich habe die Tasten nach rechts verschoben, um den Auftrag und die Tickets der Position der Tickets im Visualizer zu sehen, da sie durch die Tasten ausgeblendet werden konnten):
//--- Eingabeparameter input ulong InpMagic = 123; // Magicnummer input double InpLots = 0.1; // Losgröße input uint InpStopLoss = 50; // StopLoss in Punkten input uint InpTakeProfit = 50; // TakeProfit in Punkten input uint InpDistance = 50; // Abstand der Pending-Orders (Punkte) input uint InpDistanceSL = 50; // Abstand von StopLimit-Orders (Punkte) input uint InpSlippage = 0; // Slippage in Punkten input double InpWithdrawal = 10; // Abbuchung von Geldern (im Tester) input uint InpButtShiftX = 40; // Versatz der Schaltfläche nach X input uint InpButtShiftY = 10; // Versatz der Schaltfläche nach Y //--- Globale Variablen
Ändern wir noch leicht den Funktionscode für die Tastenbedienung:
//+------------------------------------------------------------------+ //| Erstellen des Panels mit Schaltflächen | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+
und implementieren wir den Funktionsaufruf in der Funktion OnInit():
//--- Erstellen der Schaltflächen if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Setzen der Handelsparameter
Der vollständige Code des EA ist unten zusehen.
//+------------------------------------------------------------------+ //| TestDoEasyPart05.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <Trade\Trade.mqh> //--- Enumerationen enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL }; #define TOTAL_BUTT (17) //--- Strukturen struct SDataButt { string name; string text; }; //--- Eingabeparameter input ulong InpMagic = 123; // Magicnummer input double InpLots = 0.1; // Losgröße input uint InpStopLoss = 50; // StopLoss in Punkten input uint InpTakeProfit = 50; // TakeProfit in Punkten input uint InpDistance = 50; // Abstand der Pending-Orders (Punkte) input uint InpDistanceSL = 50; // Abstand von StopLimit-Orders (Punkte) input uint InpSlippage = 0; // Slippage in Punkten input double InpWithdrawal = 10; // Abbuchung von Geldern (im Tester) input uint InpButtShiftX = 40; // Versatz der Schaltfläche nach X input uint InpButtShiftY = 10; // Versatz der Schaltfläche nach Y //--- Globale Variablen CEngine engine; CTrade trade; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Prüfen des Kontotyps if(!engine.IsHedge()) { Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge")); return INIT_FAILED; } //--- Setzen der globalen Variablen prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; //--- Erstellen der Schaltflächen if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Setzen der Handelsparameter trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Lösche Objekte ObjectsDeleteAll(0,prefix); Comment(""); } //+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- static ENUM_TRADE_EVENT last_event=WRONG_VALUE; if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); int total=ObjectsTotal(0); for(int i=0;i<total;i++) { string obj_name=ObjectName(0,i); if(StringFind(obj_name,prefix+"BUTT_")<0) continue; PressButtonEvents(obj_name); } } if(engine.LastTradeEvent()!=last_event) { Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent())); last_event=engine.LastTradeEvent(); } } //+------------------------------------------------------------------+ //| Timer Funktion | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| Funktion der Chart-Events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+ //| Erstellen des Panels mit Schaltflächen | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+ //| Erstellen der Schaltflächen | //+------------------------------------------------------------------+ bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8) { if(ObjectFind(0,name)<0) { if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) { Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); return false; } ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n"); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray); return true; } return false; } //+------------------------------------------------------------------+ //| Rückgabe des Status der Schaltflächen | //+------------------------------------------------------------------+ bool ButtonState(const string name) { return (bool)ObjectGetInteger(0,name,OBJPROP_STATE); } //+------------------------------------------------------------------+ //| Setzen des Status' der Schaltflächen | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); } //+------------------------------------------------------------------+ //| Transformieren der Enumeration in den Text der Schaltflächen | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"buy","Buy"); StringReplace(txt,"sell","Sell"); StringReplace(txt,"_limit"," Limit"); StringReplace(txt,"_stop"," Stop"); StringReplace(txt,"close_","Close "); StringReplace(txt,"2"," 1/2"); StringReplace(txt,"_by_"," by "); StringReplace(txt,"profit_","Profit "); StringReplace(txt,"delete_","Delete "); return txt; } //+------------------------------------------------------------------+ //| Bearbeiten des Klicks auf Schaltflächen | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { //--- Konvertieren der Namen der Schaltflächen in die Zeichenketten-ID string button=StringSubstr(button_name,StringLen(prefix)); //--- Falls eine Taste gedrückt wurde if(ButtonState(button_name)) { //--- Wenn die Schaltfläche BUTT_BUY geklickt wurde: Eröffnen einer Kaufposition if(button==EnumToString(BUTT_BUY)) { //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Eröffnen einer Kaufposition trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp); } //--- Falls die Schaltfläche BUTT_BUY_LIMIT geklickt wurde: Setzen von BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Setzen einer BuyLimit-Order trade.BuyLimit(lot,price_set,Symbol(),sl,tp); } //--- Falls die Schaltfläche BUTT_BUY_STOP geklickt wurde: Platzieren von BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Setzen einer BuyStop-Order trade.BuyStop(lot,price_set,Symbol(),sl,tp); } //--- Falls die Schaltfläche BUTT_BUY_STOP_LIMIT geklickt wurde: Platzieren von BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Abrufen des Preises von BuyStop relativ zu StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Berechnen des Preises der BuyLimit-Order relativ zu BuyStop unter Berücksichtigung des StopLevels double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Setzen von BuyStopLimit-Order trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- Wenn die Schaltfläche BUTT_SELL geklickt wurde: Eröffnen einer Verkaufsposition else if(button==EnumToString(BUTT_SELL)) { //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Eröffnen einer Verkaufsposition trade.Sell(lot,Symbol(),0,sl,tp); } //--- Falls die Schaltfläche BUTT_SELL_LIMIT geklickt wurde: Setzen von SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Setzen von SellLimit-Order trade.SellLimit(lot,price_set,Symbol(),sl,tp); } //--- Falls die Schaltfläche BUTT_SELL_STOP geklickt wurde: Platzieren von SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Abrufen des Preises der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Setzen von SellStop-Order trade.SellStop(lot,price_set,Symbol(),sl,tp); } //--- Falls die Schaltfläche BUTT_SELL_STOP_LIMIT geklickt wurde: Platzieren von SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Abrufen des Preises von SellStop relativ zu StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Berechnen des Preises der SellLimit-Order relativ zu SellStop unter Berücksichtigung des StopLevels double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Setzen der SellStopLimit-Order trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Abrufen der Ticketnummer der Kaufposition und Schließen der Position mittels der Ticketnummer trade.PositionClose(position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY2 geklickt wurde: Schließen der Hälfte des Kaufs mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Berechnen des zu schließenden Volumens und schließen der Hälfte der Kaufposition mittels der Ticketnummer trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY_BY_SELL geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn durch einen entgegengesetzten Verkauf else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Auswählen der Kaufposition mit Maximalgewinn COrder* position_buy=list_buy.At(index_buy); //--- Auswählen der Verkaufsposition mit Maximalgewinn COrder* position_sell=list_sell.At(index_sell); if(position_buy!=NULL && position_sell!=NULL) { //--- Schließen der Kaufposition durch eine entgegengesetzte Verkaufsposition trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Abrufen der Ticketnummer der Verkaufsposition und Schließen der Position mittels der Ticketnummer trade.PositionClose(position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL2 geklickt wurde: Schließen der Hälfte der Verkaufsposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Berechnen des zu schließenden Volumens und schließen der Hälfte der Verkaufsposition mittels der Ticketnummer trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL_BY_BUY geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn durch einen entgegengesetzten Kauf else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Auswählen der Verkaufsposition mit Maximalgewinn COrder* position_sell=list_sell.At(index_sell); //--- Auswählen der Kaufposition mit Maximalgewinn COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Schließen einer Verkaufsposition mit einer entgegengesetzten Kaufposition trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_ALL geklickt wurde: Schließen aller Positionen beginnend mit dem kleinsten Gewinn else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In der Schleife aller Positionen mit dem geringsten Gewinn for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- Schließen jeder Position mittels der Ticketnummer trade.PositionClose(position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_DELETE_PENDING geklickt wurde:: Entfernen der ersten Pending-Order else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Abrufen der Liste aller Aufträge CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Sortieren der neuen Liste nach der Platzierungszeit list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); int total=list.Total(); //--- In der Schleife aller Positionen mit der größten Zeitspanne for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- Löschen der Order mach dem Ticket trade.OrderDelete(order.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_PROFIT_WITHDRAWAL geklickt wurde: Gelder vom Konto abbuchen if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- Wenn das Programm im Tester gestartet wurde if(MQLInfoInteger(MQL_TESTER)) { //--- Emulieren eine Kontoabbuchung TesterWithdrawal(withdrawal); } } //--- Warten für 1/10 einer Sekunde Sleep(100); //--- Klicken der Schaltfläche rückgängig machen und Neuzeichnen des Charts ButtonState(button_name,false); ChartRedraw(); } } //+------------------------------------------------------------------+
Jetzt können wir den EA kompilieren und im Tester starten. Wenn wir auf die Schaltflächen klicken, werden im Testerjournal kurze zweizeilige Meldungen über auftretende Kontoereignisse angezeigt.
Einträge der Ereignisbehandlung des EAs werden im Journal nicht angezeigt, da sie außerhalb des Testers arbeiten. Wenn wir auf die Schaltflächen des EAs eines Demokontos klicken, werden im Terminaljournal drei Zeilen angezeigt: zwei Zeilen aus der Methode zur Anzeige von Kurznachrichten der Klasse CEvent und eine weitere — aus der Funktion OnChartEvent() des EAs.
Nachfolgend sehen wir ein Beispiel für die Anzeige einer Nachricht im Journal, wenn Platzieren und Entfernen einer Pending-Order:
- Pending-Order platziert: 2019.04.05 23:19:55.248 - EURUSD 0.10 Sell Limit #375419507 at price 1.14562 OnChartEvent: id=1001, event=TRADE_EVENT_PENDING_ORDER_PLASED, lparam=375419507, dparam=1.14562, sparam=EURUSD - Pending-Order entfernt: 2019.04.05 23:19:55.248 - EURUSD 0.10 Sell Limit #375419507 at price 1.14562 OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=375419507, dparam=1.14562, sparam=EURUSD
Was kommt als Nächstes?
Im nächsten Artikel werden wir anfangen, die Funktionen für die Arbeit an MetaTrader 5 Netting-Konten hinzuzufügen.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie herunterladen und
testen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.
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.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/6211
- 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.