
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil VI): Ereignisse von Änderungen von Orders und Positionen
Inhalt
- Ereignisklasse der Modifikationen
- Testen der Änderung von Orders und Positionen
- Was kommt als Nächstes?
Im vorherigen Artikel haben wir das Verfolgen der Ereignisse von StopLimit-Orders implementiert. Die gesamte Funktionsweise zur Definition der Ereignisse wurde erweiterbar gemacht, so dass wir die Suche nach anderen notwendigen Ereignissen einfach hinzufügen können.
Wir haben die Aktivierung einer StopLimit-Order als das Platzieren einer neuen offenen Order "qualifiziert", was sinnvoll ist, da eine
neue Orderart ein neues Platzierungsereignis erfordert. In diesem Artikel werden wir die Ereignisse einer etwas anderen Art verfolgen —
die Änderung der bereits bestehenden Orders und Positionen (wir haben bereits von deren Platzierung und Eröffnung erfahren, dank der
Erlangung der entsprechenden Ereignisse im Programm). Das bedeutet, dass wir noch eine weitere KLasse (der Modifikationsereignisse)
benötigen, die von der abstrakten Ereignisklasse CEvent abgeleitet ist.
Ereignisklasse der Modifikationen
Wie üblich beginnen wir mit der Vorbereitung der erforderlichen Konstanten der Enumeration.
Öffnen Sie die
Bibliotheksdatei
Defines.mqh und setzen Sie die Anzahl der übersprungenen
Eigenschaften auf Null in der Makroersetzung, die die Anzahl der
unbenutzten ganzzahligen Eigenschaften der Ordnung enthält, wenn Sie nach ihnen sortieren. Wir werden später alle Eigenschaften
der ganzzahligen Order benötigen. Bisher haben wir beim Suchen und Sortieren eine Eigenschaft übersprungen — die letzte aus der Liste der
Enumerationskonstanten:
ORDER_PROP_DIRECTION — Ordertyp nach der Richtung (denken Sie daran,
dass wir alle unbenutzten Eigenschaften an das Ende der Konstantenliste der Enumeration setzen). In den aktuellen und folgenden Artikeln
benötigen wir diese Eigenschaft für die Suche nach allen unidirektional Pending-Orders in der Kollektionsliste des Marktes.
//+------------------------------------------------------------------+ //| Order, Deal, Position, Integer-Eigenschaften | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // Order-Ticket ORDER_PROP_MAGIC, // Magicnummer des Auftrags ORDER_PROP_TIME_OPEN, // Eröffnungszeit (MQL5 Deal-Zeit) ORDER_PROP_TIME_CLOSE, // Schlusszeit (MQL5 Zeit der Ausführung oder Entfernung - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // Eröffnungszeit in Millisekunden (MQL5 Deal-Zeit in msc) ORDER_PROP_TIME_CLOSE_MSC, // Schlusszeit in Millisekunden (MQL5 Zeit der Ausführung oder Entfernung - ORDER_TIME_DONE_MSC) ORDER_PROP_TIME_EXP, // Verfallszeit der Order (für Pending-Orders) ORDER_PROP_STATUS, // Order-Status (aus der Enumeration ENUM_ORDER_STATUS) ORDER_PROP_TYPE, // Order/deal type ORDER_PROP_REASON, // Deal/Order/Position Ursache oder Quelle ORDER_PROP_STATE, // Auftragsstatus (aus der Enumeration ENUM_ORDER_STATE) ORDER_PROP_POSITION_ID, // Positions-ID ORDER_PROP_POSITION_BY_ID, // Entgegengesetzte Positions-ID ORDER_PROP_DEAL_ORDER_TICKET, // Ticket of the order that triggered a deal ORDER_PROP_DEAL_ENTRY, // Deal-Richtung – IN, OUT oder IN/OUT ORDER_PROP_TIME_UPDATE, // Zeit der Positionsänderung in Sekunden ORDER_PROP_TIME_UPDATE_MSC, // Zeit der Positionsänderung in Millisekunden ORDER_PROP_TICKET_FROM, // Ticket der Ober-Order ORDER_PROP_TICKET_TO, // Ticket der abgeleiteten Order ORDER_PROP_PROFIT_PT, // Gewinn in Points ORDER_PROP_CLOSE_BY_SL, // Flag für das Schließen durch StopLoss ORDER_PROP_CLOSE_BY_TP, // Flag für das Schließen mit TakeProfit ORDER_PROP_GROUP_ID, // Order/position group ID ORDER_PROP_DIRECTION, // Direction type (Buy, Sell) }; #define ORDER_PROP_INTEGER_TOTAL (24) // Total number of integer properties #define ORDER_PROP_INTEGER_SKIP (0) // Number of order properties not used in sorting //+------------------------------------------------------------------+
Da wir die Eigenschaft nicht mehr überspringen, müssen wir auch die Möglichkeit hinzufügen, nach ihr zu sortieren. Fügen wir das Kriterium Sortieren nach Richtung der Order zur Enumeration der möglichen Sortierkriterien von Orders und Deals hinzu:
//+------------------------------------------------------------------+ //| Mögliche Sortierkriterien von Orders und Deals | //+------------------------------------------------------------------+ #define FIRST_ORD_DBL_PROP (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP) #define FIRST_ORD_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP) 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 = 9, // Sort by order/position reason/source SORT_BY_ORDER_STATE = 10, // Sort by order status SORT_BY_ORDER_POSITION_ID = 11, // Sortieren nach der Positions-ID SORT_BY_ORDER_POSITION_BY_ID = 12, // Sortieren nach der ID der entgegengesetzten Position SORT_BY_ORDER_DEAL_ORDER = 13, // Sortieren nach dem Auftrag als Basis des Deals SORT_BY_ORDER_DEAL_ENTRY = 14, // Sortieren nach der Richtung der Deals – IN, OUT oder IN/OUT SORT_BY_ORDER_TIME_UPDATE = 15, // Sortieren nach der Änderungszeit der Position in Sekunden SORT_BY_ORDER_TIME_UPDATE_MSC = 16, // Sortieren nach der Änderungszeit der Position in Millisekunden SORT_BY_ORDER_TICKET_FROM = 17, // Sortieren nach dem Tickets der Ober-Order SORT_BY_ORDER_TICKET_TO = 18, // Sortieren nach der Ticket der abgeleiteten Order SORT_BY_ORDER_PROFIT_PT = 19, // Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL = 20, // Sortieren nach dem Flag für das Schließen durch StopLoss SORT_BY_ORDER_CLOSE_BY_TP = 21, // Sortieren nach dem Flag für das Schließen durch TakeProfit SORT_BY_ORDER_GROUP_ID = 22, // Sort by order/position group ID SORT_BY_ORDER_DIRECTION = 23, // Sort by direction (Buy, Sell) //--- Sortieren nach den Double-Eigenschaften SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, // Sort by open price SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+1, // Sort by close price SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+2, // Sort by StopLoss price SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+3, // Sort by TakeProfit price SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+4, // Sort by profit SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+5, // Sort by commission SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+6, // Sort by swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+7, // Sort by volume SORT_BY_ORDER_VOLUME_CURRENT = FIRST_ORD_DBL_PROP+8, // Sort by unexecuted volume SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+9, // Sort by profit+commission+swap criterion SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+10, // Sort by Limit order when StopLimit order is activated //--- Sortieren nach den String-Eigenschaften SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, // Sort by symbol SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+1, // Sort by comment SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+2 // Sort by order ID in an external trading system }; //+------------------------------------------------------------------+
Wie Sie sich vielleicht erinnern, verwenden wir zur Erstellung einer Ereignis-ID den Ereignis-Code, der aus einer Reihe von Flags besteht, die zusammen den Typ eines aufgetretenen Ereignisses angeben. Da wir Änderungsereignisse verfolgen werden, müssen wir der Enumeration der Handelsvermerke notwendige Flags hinzufügen:
//+------------------------------------------------------------------+ //| Liste der Flags der Handelsereignisses des Kontos | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT_FLAGS { TRADE_EVENT_FLAG_NO_EVENT = 0, // Kein Ereignis TRADE_EVENT_FLAG_ORDER_PLASED = 1, // Pending-Order platziert TRADE_EVENT_FLAG_ORDER_REMOVED = 2, // Pending-Order entfernt TRADE_EVENT_FLAG_ORDER_ACTIVATED = 4, // Pending-Order Aktivierungspreis TRADE_EVENT_FLAG_POSITION_OPENED = 8, // Position eröffnet TRADE_EVENT_FLAG_POSITION_CHANGED= 16, // Position changed TRADE_EVENT_FLAG_POSITION_REVERSE= 32, // Position reversed TRADE_EVENT_FLAG_POSITION_CLOSED = 64, // Position closed TRADE_EVENT_FLAG_ACCOUNT_BALANCE = 128, // Balance operation (clarified by a deal type) TRADE_EVENT_FLAG_PARTIAL = 256, // Partial execution TRADE_EVENT_FLAG_BY_POS = 512, // Executed by opposite position TRADE_EVENT_FLAG_PRICE = 1024, // Placement price modification TRADE_EVENT_FLAG_SL = 2048, // Executed by StopLoss TRADE_EVENT_FLAG_TP = 4096, // Executed by TakeProfit TRADE_EVENT_FLAG_ORDER_MODIFY = 8192, // Order modification TRADE_EVENT_FLAG_POSITION_MODIFY = 16384, // Position modification }; //+------------------------------------------------------------------+
Fügen wir die Ereignisse der Orderänderung und Positionsänderung
zur Liste der möglichen Handelsereignisse auf dem Konto hinzu (vorher haben wir bereits einige Ereignisse zur Liste hinzugefügt, aber dies war
eine vorläufige Erklärung der Konstanten):
//+------------------------------------------------------------------+ //| Liste der möglichen Handelsereignisse auf dem Konto | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT { TRADE_EVENT_NO_EVENT = 0, // No trading event TRADE_EVENT_PENDING_ORDER_PLASED, // Pending-Order platziert TRADE_EVENT_PENDING_ORDER_REMOVED, // Pending-Order entfernt //--- Mitglieder der Enumeration stimmen mit den Mitgliedern der Enumeration ENUM_DEAL_TYPE überein //--- (constant order below should not be changed, no constants should be added/deleted) TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT, // Accruing credit (3) TRADE_EVENT_ACCOUNT_CHARGE, // Weitere Lastschrift TRADE_EVENT_ACCOUNT_CORRECTION, // Korrekturbuchung TRADE_EVENT_ACCOUNT_BONUS, // Accruing bonuses TRADE_EVENT_ACCOUNT_COMISSION, // Zusätzliche Kommission TRADE_EVENT_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a trading day TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY, // Kommission des Agenten belastet am Tagesende TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY, // Kommission des Agenten belastet am Monatsende TRADE_EVENT_ACCOUNT_INTEREST, // Accrued interest on free funds TRADE_EVENT_BUY_CANCELLED, // Stornierter Kauf-Deal TRADE_EVENT_SELL_CANCELLED, // Stornierter Verkaufs-Deal TRADE_EVENT_DIVIDENT, // Accruing dividends TRADE_EVENT_DIVIDENT_FRANKED, // Accruing franked dividends TRADE_EVENT_TAX = DEAL_TAX, // Tax //--- constants related to the DEAL_TYPE_BALANCE deal type from the DEAL_TYPE_BALANCE enumeration TRADE_EVENT_ACCOUNT_BALANCE_REFILL = DEAL_TAX+1, // Replenishing account balance TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2, // Withdrawing funds from an account //--- Remaining possible trading events //--- (constant order below can be changed, constants can be added/deleted) TRADE_EVENT_PENDING_ORDER_ACTIVATED = DEAL_TAX+3, // Pending order activated by price TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL, // Pending-Order, teilweise ausgelöst durch den Preis TRADE_EVENT_POSITION_OPENED, // Position eröffnet TRADE_EVENT_POSITION_OPENED_PARTIAL, // Position teilweise eröffnet TRADE_EVENT_POSITION_CLOSED, // Position geschlossen TRADE_EVENT_POSITION_CLOSED_BY_POS, // Position closed partially TRADE_EVENT_POSITION_CLOSED_BY_SL, // Position geschlossen duch Stop-Loss TRADE_EVENT_POSITION_CLOSED_BY_TP, // Position geschlossen durch Take-Profit TRADE_EVENT_POSITION_REVERSED_BY_MARKET, // Position reversal by a new deal (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING, // Position reversal by activating a pending order (netting) TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL, // Position reversal by partial market order execution (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL, // Position reversal by partial pending order activation (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET, // Added volume to a position by a new deal (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL, // Added volume to a position by partial activation of a market order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING, // Added volume to a position by activating a pending order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL, // Added volume to a position by partial activation of a pending order (netting) TRADE_EVENT_POSITION_CLOSED_PARTIAL, // Position teilweise geschlossen TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS, // Position closed partially by an opposite one TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL, // Position teilweise geschlossen duch Stop-Loss TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP, // Position teilweise geschlossen durch Take-Profit TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER, // StopLimit order activation TRADE_EVENT_MODIFY_ORDER_PRICE, // Changing order price TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS, // Changing order and StopLoss price TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT, // Changing order and TakeProfit price TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT, // Changing order, StopLoss and TakeProfit price TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT, // Changing order's StopLoss and TakeProfit price TRADE_EVENT_MODIFY_ORDER_STOP_LOSS, // Changing order's StopLoss TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT, // Changing order's TakeProfit TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT, // Changing position's StopLoss and TakeProfit TRADE_EVENT_MODIFY_POSITION_STOP_LOSS, // Changing position's StopLoss TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT, // Changing position's TakeProfit }; //+------------------------------------------------------------------+
Da wir die neue Klasse entwickeln werden, die von der abstrakten Ereignisklasse CEvent abgeleitet ist, müssen wir für die neu abgeleitete
Klasse noch einen weiteren
Ereignisstatus — "modification" setzen. Fügen wir das der Liste der
Enumeration mit den Ereignisstatus' 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, // Historical position event (closing EVENT_STATUS_BALANCE, // Ereignis einer Saldenoperation (Gutschrift, Abbuchung von Geldern und Ereignisse vom Typ aus der Enumeration ENUM_DEAL_TYPE) EVENT_STATUS_MODIFY // Order/position modification event }; //+------------------------------------------------------------------+
Fügen wir die Ereignisursache "modification" der Liste der Enumeration mit den Ereignisstatus' hinzu:
//+------------------------------------------------------------------+ //| Ereignisursache | //+------------------------------------------------------------------+ enum ENUM_EVENT_REASON { EVENT_REASON_REVERSE, // Position reversal (netting) EVENT_REASON_REVERSE_PARTIALLY, // Position reversal by partial request execution (netting) EVENT_REASON_REVERSE_BY_PENDING, // Position reversal by pending order activation (netting) EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY, // Position reversal in case of a pending order partial execution (netting) //--- All constants related to a position reversal should be located in the above list EVENT_REASON_ACTIVATED_PENDING, // Pending order activation EVENT_REASON_ACTIVATED_PENDING_PARTIALLY, // Pending order partial activation EVENT_REASON_STOPLIMIT_TRIGGERED, // StopLimit order activation EVENT_REASON_MODIFY, // Modification EVENT_REASON_CANCEL, // Cancelation EVENT_REASON_EXPIRED, // Order expiration EVENT_REASON_DONE, // Request executed in full EVENT_REASON_DONE_PARTIALLY, // Request executed partially EVENT_REASON_VOLUME_ADD, // Add volume to a position (netting) EVENT_REASON_VOLUME_ADD_PARTIALLY, // Add volume to a position by a partial request execution (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING, // Add volume to a position when a pending order is activated (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY, // Add volume to a position when a pending order is partially executed (netting) EVENT_REASON_DONE_SL, // Closing by StopLoss EVENT_REASON_DONE_SL_PARTIALLY, // Partial closing by StopLoss EVENT_REASON_DONE_TP, // Closing by TakeProfit EVENT_REASON_DONE_TP_PARTIALLY, // Partial closing by TakeProfit EVENT_REASON_DONE_BY_POS, // Closing by an opposite position EVENT_REASON_DONE_PARTIALLY_BY_POS, // Partial closing by an opposite position EVENT_REASON_DONE_BY_POS_PARTIALLY, // Partial closing by an opposite position EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY, // Closing an opposite position by a partial volume //--- Konstanten bezüglich des Dealtyps DEAL_TYPE_BALANCE aus der Enumeration ENUM_DEAL_TYPE EVENT_REASON_BALANCE_REFILL, // Refilling the balance EVENT_REASON_BALANCE_WITHDRAWAL, // Withdrawing funds from the account //--- Liste der Konstanten bezüglich der Enumeration TRADE_EVENT_ACCOUNT_CREDIT aus der Enumeration ENUM_TRADE_EVENT und verschoben auf +13 relativ zu ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3) EVENT_REASON_ACCOUNT_CREDIT, // Accruing credit EVENT_REASON_ACCOUNT_CHARGE, // Additional charges EVENT_REASON_ACCOUNT_CORRECTION, // Correcting entry EVENT_REASON_ACCOUNT_BONUS, // Accruing bonuses EVENT_REASON_ACCOUNT_COMISSION, // Additional commissions EVENT_REASON_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY, // Agent commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY, // Agent commission charged at the end of a month EVENT_REASON_ACCOUNT_INTEREST, // Accruing interest on free funds EVENT_REASON_BUY_CANCELLED, // Canceled buy deal EVENT_REASON_SELL_CANCELLED, // Canceled sell deal EVENT_REASON_DIVIDENT, // Accruing dividends EVENT_REASON_DIVIDENT_FRANKED, // Accruing franked dividends EVENT_REASON_TAX // Tax }; #define REASON_EVENT_SHIFT (EVENT_REASON_ACCOUNT_CREDIT-3) //+------------------------------------------------------------------+
Da wir immer wissen wollen, wie sich die Order-/Positionseigenschaften geändert haben, sollten wir Order-/Positionseigenschaftspreise vor der Modifikation (Postmodifikationspreise werden aus den bereits vorhandenen Eigenschaften übernommen) und Eigenschaften zum Schreiben der aktuellen Preise während eines Ereignisses zu den ganzzahligen Eigenschaften des Ereignisses hinzufügen, sowie die Anzahl der realen Eigenschaften des Ereignisses von 10 auf 15 ändern und die Anzahl der unbenutzten Eigenschaften während der Suche und Sortierung hinzufügen (Änderungsdaten und Preise während eines Änderungsereignisses werden nicht für die Suche und Sortierung verwendet):
//+------------------------------------------------------------------+ //| Double-Eigenschaften von Ereignissen | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_DOUBLE { EVENT_PROP_PRICE_EVENT = EVENT_PROP_INTEGER_TOTAL, // Price an event occurred at 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_ORDER_INITIAL, // Requested order volume EVENT_PROP_VOLUME_ORDER_EXECUTED, // Executed order volume EVENT_PROP_VOLUME_ORDER_CURRENT, // Remaining order volume EVENT_PROP_VOLUME_POSITION_EXECUTED, // Current executed position volume after a deal EVENT_PROP_PROFIT, // Profit //--- EVENT_PROP_PRICE_OPEN_BEFORE, // Order price before modification EVENT_PROP_PRICE_SL_BEFORE, // StopLoss price before modification EVENT_PROP_PRICE_TP_BEFORE, // TakeProfit price before modification EVENT_PROP_PRICE_EVENT_ASK, // Ask price during an event EVENT_PROP_PRICE_EVENT_BID, // Bid price during an event }; #define EVENT_PROP_DOUBLE_TOTAL (15) // Total number of event's real properties #define EVENT_PROP_DOUBLE_SKIP (5) // Number of order properties not used in sorting //+------------------------------------------------------------------+
Lassen Sie uns die Berechnung der entsprechenden Makro-Substitution
ändern, um den Index der ersten String-Eigenschaft des Ereignisses in der Enumeration der Sortierkriterien für Ereignisse korrekt zu
finden:
//+------------------------------------------------------------------+ //| Mögliche Sortierkriterien von Ereignissen | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_EVENTS_MODE { //--- Sortieren nach den Integer-Eigenschaften SORT_BY_EVENT_TYPE_EVENT = 0, // Sort by event type SORT_BY_EVENT_TIME_EVENT = 1, // Sort by event time SORT_BY_EVENT_STATUS_EVENT = 2, // Sort by event status (from the ENUM_EVENT_STATUS enumeration) SORT_BY_EVENT_REASON_EVENT = 3, // Sort by event reason (from the ENUM_EVENT_REASON enumeration) SORT_BY_EVENT_TYPE_DEAL_EVENT = 4, // Sort by deal event type SORT_BY_EVENT_TICKET_DEAL_EVENT = 5, // Sort by deal event ticket SORT_BY_EVENT_TYPE_ORDER_EVENT = 6, // Sort by type of an order, based on which a deal event is opened (the last position order) SORT_BY_EVENT_TICKET_ORDER_EVENT = 7, // Sort by type of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TIME_ORDER_POSITION = 8, // Sort by time of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TYPE_ORDER_POSITION = 9, // Sort by type of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TICKET_ORDER_POSITION = 10, // Sort by a ticket of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_POSITION_ID = 11, // Sort by position ID SORT_BY_EVENT_POSITION_BY_ID = 12, // Sort by opposite position ID SORT_BY_EVENT_MAGIC_ORDER = 13, // Sort by order/deal/position magic number SORT_BY_EVENT_MAGIC_BY_ID = 14, // Sort by opposite position magic number //--- Sortieren nach den Double-Eigenschaften SORT_BY_EVENT_PRICE_EVENT = FIRST_EVN_DBL_PROP, // Sort by a price an event occurred at SORT_BY_EVENT_PRICE_OPEN = FIRST_EVN_DBL_PROP+1, // Sort by position open price SORT_BY_EVENT_PRICE_CLOSE = FIRST_EVN_DBL_PROP+2, // Sort by position close price SORT_BY_EVENT_PRICE_SL = FIRST_EVN_DBL_PROP+3, // Sort by position's StopLoss price SORT_BY_EVENT_PRICE_TP = FIRST_EVN_DBL_PROP+4, // Sort by position's TakeProfit price SORT_BY_EVENT_VOLUME_ORDER_INITIAL = FIRST_EVN_DBL_PROP+5, // Sort by initial volume SORT_BY_EVENT_VOLUME_ORDER_EXECUTED = FIRST_EVN_DBL_PROP+6, // Sort by the current volume SORT_BY_EVENT_VOLUME_ORDER_CURRENT = FIRST_EVN_DBL_PROP+7, // Sort by remaining volume SORT_BY_EVENT_VOLUME_POSITION_EXECUTED = FIRST_EVN_DBL_PROP+8, // Sort by remaining volume SORT_BY_EVENT_PROFIT = FIRST_EVN_DBL_PROP+9, // Sort by profit //--- Sortieren nach den String-Eigenschaften SORT_BY_EVENT_SYMBOL = FIRST_EVN_STR_PROP, // Sort by order/position/deal symbol SORT_BY_EVENT_SYMBOL_BY_ID // Sort by an opposite position symbol }; //+------------------------------------------------------------------+
Lassen Sie uns die abstrakte Ereignisklasse CEvent verbessern.
Da wir Daten im Journal in den Klassen anzeigen, die aus dem abstrakten CEvent Ereignis abgeleitet sind, müssen wir die Anzahl der
Dezimalstellen der Kurse des Symbols kennen, bei dem das Ereignis bei aufgetreten ist - des Symbols Digits(). Um die nicht jedes Mal in jeder
der abgeleiteten Klassen abzurufen, holen wir sie uns einfach einmal von der Elternklasse.
Deklarieren Sie im privaten Klassenabschnitt die Klassenvariable zum Speichern des Ertes von Digits() des Ereignissymbols und initialisieren Sie diese Variable in der Initialisierungsliste des Klassenkonstruktors:
//+------------------------------------------------------------------+ //| 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 bool m_is_hedge; // Hedging account flag long m_chart_id; // Chart-ID des Steuerprogramms int m_digits; // Symbol Digits() 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 //--- Return the flag presence in the trading event 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){;} //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Fügen Sie die Beschreibungen der neuen Ereigniseigenschaften zu den Methoden hinzu, die Beschreibungen der Eigenschaften von integer und real zurückgeben:
//+------------------------------------------------------------------+ //| Liefert die Beschreibung der Integer-Eigenschaft des Ereignisses | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event's 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_MAGIC_BY_ID ? TextByLanguage("Магический номер встречной позиции","Magic number of opposite position")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORD_POS_BEFORE ? TextByLanguage("Тип ордера позиции до смены направления","Type order of position before changing direction")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORD_POS_BEFORE ? TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORD_POS_CURRENT ? TextByLanguage("Тип ордера текущей позиции","Type order of current position")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORD_POS_CURRENT ? TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position")+": #"+(string)this.GetProperty(property) : EnumToString(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_ORDER_INITIAL ? TextByLanguage("Начальный объём ордера","Order initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_EXECUTED ? TextByLanguage("Исполненный объём ордера","Order executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_CURRENT ? TextByLanguage("Оставшийся объём ордера","Order remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_POSITION_EXECUTED ? TextByLanguage("Текущий объём позиции","Position current volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : property==EVENT_PROP_PRICE_OPEN_BEFORE ? TextByLanguage("Цена открытия до модификации","Price open before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL_BEFORE ? TextByLanguage("Цена StopLoss до модификации","StopLoss price before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP_BEFORE ? TextByLanguage("Цена TakeProfit до модификации","TakeProfit price before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_EVENT_ASK ? TextByLanguage("Цена Ask в момент события","Ask price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_EVENT_BID ? TextByLanguage("Цена Bid в момент события","Bid price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : EnumToString(property) ); } //+------------------------------------------------------------------+
Fügen wir Ereignis Abwesenheitsbeschreibung, Beschreibungen neu hinzugefügter Ereignisse und Beschreibung eines unbekannten Ereignisses zur Methode hinzu, die Namen der Handelsereignisse zurückgibt:
//+------------------------------------------------------------------+ //| Rückgabe des Namens des Handelsereignisses | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_NO_EVENT ? TextByLanguage("Нет торгового события","No trading event") : 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 open") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position open 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_BY_MARKET ? TextByLanguage("Разворот позиции по рыночному запросу","Position reversal by market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING ? TextByLanguage("Разворот позиции срабатыванием отложенного ордера","Position reversal by triggering pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET ? TextByLanguage("Добавлен объём к позиции по рыночному запросу","Added volume to position by market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activation of pending order") : event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ? TextByLanguage("Разворот позиции частичным исполнением запроса","Position reversal by partial completion of market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ? TextByLanguage("Разворот позиции частичным срабатыванием отложенного ордера","Position reversal by partial activation of pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ? TextByLanguage("Добавлен объём к позиции частичным исполнением запроса","Added volume to position by partial completion of market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by partial activation of pending order") : event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER ? TextByLanguage("Сработал StopLimit-ордер","StopLimit order triggered.") : event==TRADE_EVENT_MODIFY_ORDER_PRICE ? TextByLanguage("Модифицирована цена установки ордера ","Order price modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS ? TextByLanguage("Модифицированы цена установки и StopLoss ордера","Order price and StopLoss modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT ? TextByLanguage("Модифицированы цена установки и TakeProfit ордера","Order price and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цена установки, StopLoss и TakeProfit ордера","Order price, StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цены StopLoss и TakeProfit ордера","Order StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS ? TextByLanguage("Модифицирован StopLoss ордера","Order StopLoss modified") : event==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT ? TextByLanguage("Модифицирован TakeProfit ордера","Order TakeProfit modified") : event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цены StopLoss и TakeProfit позиции","Position StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS ? TextByLanguage("Модифицирован StopLoss позиции","Position StopLoss modified") : event==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT ? TextByLanguage("Модифицирован TakeProfit позиции","Position TakeProfit modified") : EnumToString(event) ); } //+------------------------------------------------------------------+
Fügen wir zwei neue Ursachen zu der Methode hinzu, die die Beschreibung der Ereignisursachen zurückgibt:
//+------------------------------------------------------------------+ //| Rückgabe des Namens der Ursache 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_STOPLIMIT_TRIGGERED ? TextByLanguage("Срабатывание StopLimit-ордера","StopLimit order triggered") : reason==EVENT_REASON_MODIFY ? TextByLanguage("Модификация","Modified") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request") : reason==EVENT_REASON_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : reason==EVENT_REASON_VOLUME_ADD_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to the position by request partial completion") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activating pending order") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by partial activation of pending order") : reason==EVENT_REASON_REVERSE ? TextByLanguage("Разворот позиции","Position reversal") : reason==EVENT_REASON_REVERSE_PARTIALLY ? TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partial completion of request") : reason==EVENT_REASON_REVERSE_BY_PENDING ? TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal on triggered pending order") : reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY ? TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on partially triggered pending order") : 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("Снятие средств с баланса","Withdrawal from the 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) ); } //+------------------------------------------------------------------+
Fügen wir die Methoden hinzu, die die neuen Eigenschaften zurückgibt, die dem Abschnitt des vereinfachten Zugriffs auf Ereigniseigenschaften im Abschnitt 'public' der Klasse hinzugefügt wurden:
//+------------------------------------------------------------------+ //| 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-Ursache, (5) Dealtyp, (6) Deal-Ticket, //--- (7) Auftragstyp des dadurch geöffneten Deal-Ereignisses, (8) Auftragstyp der Positionseröffnung, (9) letztes Auftragsticket der Position, //--- (10) position first order ticket, (11) position ID, (12) opposite position ID, (13) magic number, (14) opposite position magic number, (15) position open time 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); } ENUM_DEAL_TYPE TypeDeal(void) const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } ENUM_ORDER_TYPE TypeOrderEvent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } ENUM_ORDER_TYPE TypeFirstOrderPosition(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketFirstOrderPosition(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 MagicCloseBy(void) const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- When changing position direction, return (1) previous position order type, (2) previous position order ticket //--- (3) current position order type, (4) current position order ticket //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);} ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); } //--- Rückgabe von (1) Preis bei den ein Ereignis auftritt, (2) Eröffnungspreis, (3) Schließpreis, //--- (4) StopLoss price, (5) TakeProfit price, (6) profit, (7) requested order volume, //--- (8) executed order volume, (9) remaining order volume, (10) executed position volume 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 VolumeOrderInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL); } double VolumeOrderExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED); } double VolumeOrderCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT); } double VolumePositionExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED); } //--- When modifying prices, return (1) order price, (2) StopLoss and (3) TakeProfit before modification double PriceOpenBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN_BEFORE); } double PriceStopLossBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL_BEFORE); } double PriceTakeProfitBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP_BEFORE); } double PriceEventAsk(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_ASK); } double PriceEventBid(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_BID); } //--- Return the (1) symbol and (2) opposite position symbol string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+
Da die meisten Klasseneigenschaften in der Sammelklasse der Ereignisse durch die Methode CreateNewEvent() ausgefüllt werden und dann ein Ereignistyp durch Aufruf der Methode SetTypeEvent() der Klasse CEvent gesetzt wird, erfolgt das Festlegen des Wertes durch Digits() des Ereignissymbols in der Methode SetTypeEvent() der Klasse CEvent zusammen mit der Definition von Änderungsereignissen:
//+------------------------------------------------------------------+ //| Dekodieren des Ereigniscodes und setzen des Handelsereignisses | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Set event symbol Digits() this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- 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; } //--- Pending order is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_MODIFY)) { //--- If the placement price is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_PRICE)) { this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE; //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT; } //--- If the placement price is not modified else { //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If a position is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_MODIFY)) { //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT; 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)) { //--- If an existing position is changed if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED)) { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial closure flag and set the //--- "position reversal by activation of a pending order" or "position reversal by partial activation of a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_PENDING : TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set the //--- "added volume to a position by activating a pending order" or "added volume to a position by partially activating a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- If a position was changed by a market deal else { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial opening flag and set the "position reversal" or "position reversal by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_MARKET : TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set "added volume to a position" or "added volume to a position by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } } //--- If a new position is opened else { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- check the partial opening flag and set "pending order activated" or "pending order partially activated" trading event 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; } } //+------------------------------------------------------------------+
Die Codekommentare in der Methodenauflistung beschreiben alle notwendigen Prüfungen und Aktionen, so dass es keinen Sinn macht, sich mit
den bereits kommentierten Aktionen zu beschäftigen. Ich glaube, hier ist alles ganz einfach und ist leicht zu verstehen.
Damit ist die Verbesserung der abstrakten Ereignisklasse abgeschlossen.
Mit Blick auf die Zukunft ist zu beachten, dass es bei der Überprüfung der Verfolgung der Preisänderung für die Platzierung
ausstehender Aufträge in einem Test EA notwendig wurde, eine Oder zu finden, die am weitesten vom Preis entfernt ist. Als ich mir die
Eigenschaften der Orders ansah, wurde mir klar, dass die Bibliothek dafür keine schnelle und vielseitige Lösung hat. Daher werden wir
eine der zusätzlichen ganzzahligen Eigenschaften der Orders verwenden — Gewinn in Punkten. Bei Pending-Orders ist dies die
Entfernung einer Order vom Preis in Punkten. Um also die Order zu finden, die am weitesten vom Preis entfernt ist, müssen wir nur nach
einer Order mit dem höchsten "Gewinn" (Abstand) in Punkten suchen.
Dieser Fall ist vergleichbar mit der Suche nach allen Pending-Orders gemäß der Richtung. Um eine Pending-Order zu finden, die vom
Preis am weitesten entfernt ist, wählen wir alle Orders derselben Richtung und sortieren die erhaltene Liste nach dem größten
Abstand. Dadurch erhalten wir eine Order von allen mit unterschiedlichen Arten, allerdings in einer Richtung (BuyLimit, BuyStop und
BuyStopLimit sind alle Kaufaufträge. Das Gegenteil gilt für Verkaufen.
Lassen Sie uns die Methode zum Erhalten der Art der Order nach ihrer Richtung in der Auflistung der abstrakten Orderklasse Order.mqh ändern:
//+------------------------------------------------------------------+ //| Rückgabe des Gewinns der Order in Points | //+------------------------------------------------------------------+ int COrder::ProfitInPoints(void) const { MqlTick tick={0}; string symbol=this.Symbol(); if(!::SymbolInfoTick(symbol,tick)) return 0; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)this.TypeOrder(); double point=::SymbolInfoDouble(symbol,SYMBOL_POINT); if(type==ORDER_TYPE_CLOSE_BY || point==0) return 0; if(this.Status()==ORDER_STATUS_HISTORY_ORDER) return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0); else if(this.Status()==ORDER_STATUS_MARKET_POSITION) { if(type==ORDER_TYPE_BUY) return int((tick.bid-this.PriceOpen())/point); else if(type==ORDER_TYPE_SELL) return int((this.PriceOpen()-tick.ask)/point); } else if(this.Status()==ORDER_STATUS_MARKET_PENDING) { if(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_STOP_LIMIT) return (int)fabs((tick.bid-this.PriceOpen())/point); else if(type==ORDER_TYPE_SELL_LIMIT || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_STOP_LIMIT) return (int)fabs((this.PriceOpen()-tick.ask)/point); } return 0; } //+------------------------------------------------------------------+
Hier fügen wir im Wesentlichen eine Prüfung auf Pending-Orders hinzu und geben den Abstand vom Preis der Order zum aktuellen Preis in Punkten zurück.
Fügen wir die Anzeige des Abstands vom Preis zur Pending-Order in Punkten zum Verfahren zur Beschreibung ganzzahliger Eigenschaften der abstrakten Orderklasse hinzu:
//+------------------------------------------------------------------+ //| Rückgabe der Beschreibung der Integer-Eigenschaft des Auftrags | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) { return ( //--- Allgemeine Eigenschaften property==ORDER_PROP_MAGIC ? TextByLanguage("Магик","Magic")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET ? TextByLanguage("Тикет","Ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_FROM ? TextByLanguage("Тикет родительского ордера","Parent order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_TO ? TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN ? TextByLanguage("Время открытия","Time open")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_CLOSE ? TextByLanguage("Время закрытия","Close time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_EXP ? TextByLanguage("Дата экспирации","Expiration date")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : (this.GetProperty(property)==0 ? TextByLanguage(": Не задана",": Not set") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)) ) : property==ORDER_PROP_TYPE ? TextByLanguage("Тип","Type")+": "+this.TypeDescription() : property==ORDER_PROP_DIRECTION ? TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() : property==ORDER_PROP_REASON ? TextByLanguage("Причина","Reason")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetReasonDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ORDER_TICKET ? TextByLanguage("Сделка на основании ордера с тикетом","Deal by order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ENTRY ? TextByLanguage("Направление сделки","Deal entry")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetEntryDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN_MSC ? TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_CLOSE_MSC ? TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_UPDATE ? TextByLanguage("Время изменения позиции","Position change time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0") ) : property==ORDER_PROP_TIME_UPDATE_MSC ? TextByLanguage("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ? TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" : "0") ) : property==ORDER_PROP_STATE ? TextByLanguage("Состояние","Statе")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StateDescription()+"\"" ) : //--- Zusätzliche Eigenschaften property==ORDER_PROP_STATUS ? TextByLanguage("Статус","Status")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StatusDescription()+"\"" ) : property==ORDER_PROP_PROFIT_PT ? ( this.Status()==ORDER_STATUS_MARKET_PENDING ? TextByLanguage("Дистанция от цены в пунктах","Distance from price in points") : TextByLanguage("Прибыль в пунктах","Profit in points") )+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_CLOSE_BY_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_CLOSE_BY_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_GROUP_ID ? TextByLanguage("Идентификатор группы","Group ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
Hier überprüfen wir den Orderstatus und wenn es sich um eine bestehende
Pending-Order handelt, wird eine Nachricht über den Abstand angezeigt,
ansonsten
eine Nachricht über den Gewinn wird in Punkten.
Damit sind die Änderungen in der abstrakten Orderklasse abgeschlossen.
Jetzt müssen wir eine weitere Klasse erstellen, die von der abstrakten Ereignisklasse CEvent abgeleitet wird. Dies ist eine Ereignisklasse der Änderungen.
Bei der Implementierung der Arbeit an Netting-Konten im sechsten Artikel
haben wir das Ereignis der Positionsöffnungsklasse verbessert: Die Klasse CEventPositionOpen verfügt seit her über die Methode, einen
kurzen Nachrichtentext in Abhängigkeit vom Ereignisstatus und dem Vorhandensein einiger Eigenschaften von Ereignisobjekten zu
erstellen.
Wenn wir ein neues Modifikationsereignis erstellen, tun wir dasselbe — Überprüfen des Modifikationsereignistyps und Erstellen
eines Ereignistextes in Abhängigkeit vom erhaltenen Typ. Außerdem müssen wir beim Senden eines Ereignisses an die Regelprogrammkarte
den zu übergebenden Preis im
dparam-Parameter der Funktion EventChartCustom() definieren.
In der Positionsereigniskategorie haben wir diesen Parameter verwendet, um den Eröffnungspreis zu übergeben, während in der
Modifikations-Ereigniskategorie mehrere Preisänderungsoptionen möglich sind, und wir müssen uns für die Preise entscheiden, die wir
in
dem Parameter dparam des Benutzerereignisses senden sollen:
- Nur ein Orderpreis wurde geändert — senden des neuen Preises der Pending-Order,
- Die Preise einer Order und von StopLoss wurden geändert — senden des neuen Preises der Pending-Order,
- Die Preise einer Order und von TakeProfit wurden geändert — senden des neuen Preises der Pending-Order,
- Die Preise einer Order, StopLoss und TakeProfit wurden geändert — senden des neuen Preises der Pending-Order,
- Der Preis einer StopLoss-Order wurde geändert — senden des neuen Preises der Pending-Order,
- Der Preis einer TakeProfit-Order wurde geändert — senden des neuen Preises der Pending-Order,
- Der StopLoss einer Position wurde geändert — senden des neuen Preises für StopLoss,
- Der TakeProfit einer Position wurde geändert — senden des neuen Preises für TakeProfit,
- Der TakeProfit und StopLoss einer Position wurden geändert — senden des Eröffnungspreises,
Wie wir sehen können, geben wir bei der Änderung eines einzelnen Preises den geänderten Preis an das Ereignis weiter. Wenn sich mehrere Preise gleichzeitig ändern, senden wir den Eröffnungspreis der Position oder den Preis der Order (der wiederum auch geändert werden kann). In einem nutzerdefinierten Programm können Sie die Änderung jedes der Preise (während seiner gleichzeitigen Änderung) durch die Art des aufgetretenen Änderungsereignisses klären.
Erstellen Sie in der neuen Datei EventModify.mqh des Ordners \MQL5\Include\DoEasy\Objects\Events die neue
Klasse
CEventModify.
Setzen wir die abstrakte Ereignisklasse CEvent als
Basisklasse dafür.
Vergessen wir nicht, die Datei der
abstrakten Ereignisklasse in die Datei der Modifikationsklasse einzubinden.
Da die Klasse relativ klein ist, werde
ich Ihnen hier die vollständige Auflistung zum Studium zur Verfügung stellen. Ich habe bereits eine ähnliche Klasse
im sechsten Teil der Bibliotheksbeschreibung beschrieben, als
ich Änderungen in der Klasse
CEventPositionOpen
implementierte.
//+------------------------------------------------------------------+ //| EventModify.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 CEventModify : public CEvent { private: double m_price; // Price passed to an event //--- Create and return a brief event description string EventsMessage(void); public: //--- Konstructor CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0) {} //--- Unterstützte Order-Eigenschaften (1) real, (2) integer virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Anzeige einer kurzen Nachricht über das Ereignis im Journal, (2) Senden des Ereignisses an den Chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Rückgabe von 'true', wenn das Ereignis die übergebene | //| Integer-Eigenschaft unterstützt, sonst 'false' | //+------------------------------------------------------------------+ bool CEventModify::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 CEventModify::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Anzeige einer kurzen Nachricht über das Ereignis im Journal | //+------------------------------------------------------------------+ void CEventModify::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Senden des Ereignisses an das Chart | //+------------------------------------------------------------------+ void CEventModify::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.m_price,this.Symbol()); } //+------------------------------------------------------------------+ //| Create and return a brief event message | //+------------------------------------------------------------------+ string CEventModify::EventsMessage(void) { //--- (1) header, (2) magic number string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string text=""; //--- Pending order price is modified if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+magic; this.m_price=this.PriceOpen(); } //--- Pending order price and StopLoss are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" StopLoss: "+sl+magic; this.m_price=this.PriceOpen(); } //--- Pending order price and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Pending order price, as well as its StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+", StopLoss: "+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Pending order StopLoss is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic; this.m_price=this.PriceStopLoss(); } //--- Pending order TakeProfit is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic; this.m_price=this.PriceTakeProfit(); } //--- Pending order StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Position StopLoss is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic; this.m_price=this.PriceStopLoss(); } //--- Position TakeProfit is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic; this.m_price=this.PriceTakeProfit(); } //--- Position StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } return head+this.Symbol()+" "+text; } //+------------------------------------------------------------------+
Jetzt müssen wir Ereignisse definieren, bei denen wir die bereits vorhandenen Orders und Positionen ändern, ein neues Ereignis erstellen und es in der Klasse mit der Liste der Ereigniskollektion hinzufügen.
Lassen Sie uns die notwendigen Verbesserungen der Klasse CEventsCollection in der Datei EventsCollection.mqh aus dem
Verzeichnis der Bibliothek \MQL5\Include\DoEasy\Collections implementieren.
Einbinden der Datei der neuen Ereignisklasse der Änderungen.
Deklarieren wir im Abschnitt 'private' der Klasse die Klassenvariable —
die Struktur zum Speichern der Tickdaten. Sie dient dazu, Daten über die zuletzt geänderten Preise der Ereignisse zu erhalten.
//+------------------------------------------------------------------+ //| 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" #include "..\Objects\Events\EventModify.mqh" //+------------------------------------------------------------------+ //| Account event collection | //+------------------------------------------------------------------+ class CEventsCollection : public CListObj { private: CListObj m_list_events; // Event list bool m_is_hedge; // Hedging account flag long m_chart_id; // Chart-ID des Steuerprogramms int m_trade_event_code; // Trading event code ENUM_TRADE_EVENT m_trade_event; // Handelsereignis auf dem Konto CEvent m_event_instance; // Ereignisobjekt für die Suche nach einer Eigenschaft MqlTick m_tick; // Last tick structure
Initialisieren der Tickstruktur im Klassenkonstruktor:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_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(); ::ZeroMemory(this.m_tick); } //+------------------------------------------------------------------+
Im siebten Teil der Bibliotheksbeschreibung haben wir eine
überladene Methode zum Erstellen eines neuen Ereignisses entwickelt. Jetzt haben wir zwei davon — die Methode zum Erstellen von
Ereignissen beim Ändern der Anzahl der Orders und Positionen auf dem Konto und die Methode zum Erstellen eines neuen Ereignisses beim
Ändern einer bereits bestehenden Order oder Position.
Die zweite Methode sollte verbessert werden, so dass sie in der Lage ist, Order- und Positionsänderungsereignisse zu verfolgen
(im siebten Teil verarbeitet die Methode nur ein StopLimit-Orderaktivierungsereignis).
Fügen wir die Codezeilen hinzu, die ein Modifikationsereignis für
Orders/Positionen behandeln und Speichern von Eigenschaften für
Orders/Positionen vor der Modifikation:
//+------------------------------------------------------------------+ //| Create a trading event depending on the order change type | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrderControl* order) { if(!::SymbolInfoTick(order.Symbol(),this.m_tick)) { Print(DFUN,TextByLanguage("Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "),order.Symbol()); return; } CEvent* event=NULL; //--- Pending StopLimit order activated if(order.GetChangeType()==CHANGE_TYPE_ORDER_TYPE) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket()); } //--- Modification else { //--- Pending order price is modified if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE; //--- Pending order price and StopLoss are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL; //--- Pending order price and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_TP; //--- Pending order price, as well as its StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Pending order StopLoss is modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL; //--- Pending order TakeProfit is modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_TP; //--- Pending order StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Position StopLoss is modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL; //--- Position TakeProfit is modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_TP; //--- Position StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Create a modification event event=new CEventModify(this.m_trade_event_code,order.Ticket()); } //--- Create an event if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.Time()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_STOPLIMIT_TRIGGERED); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,PositionTypeByOrderType((ENUM_ORDER_TYPE)order.TypeOrderPrev())); // Type of the order that triggered an event event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket of the order that triggered an event event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Event order type event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // First position order type event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // First position order ticket event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Positions-ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,0); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,0); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrderPrev()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_PRICE_OPEN_BEFORE,order.PricePrev()); // Order price before modification event.SetProperty(EVENT_PROP_PRICE_SL_BEFORE,order.StopLossPrev()); // StopLoss price before modification event.SetProperty(EVENT_PROP_PRICE_TP_BEFORE,order.TakeProfitPrev()); // TakeProfit price before modification event.SetProperty(EVENT_PROP_PRICE_EVENT_ASK,this.m_tick.ask); // Ask price during an event event.SetProperty(EVENT_PROP_PRICE_EVENT_BID,this.m_tick.bid); // Bid price during an event event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimePrev()); // Position first order time event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PricePrev()); // Price the event occurred at event.SetProperty(EVENT_PROP_PRICE_OPEN,order.Price()); // Order placement price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.Price()); // Order close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Order StopLoss price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Order TakeProfit price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,0); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.Volume()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,0); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Symbol des Auftrags event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Setzen der Chart-ID des Steuerprogramms, dekodieren des Ereigniscodes und setzen des Ereignis-Typs event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Hinzufügen einer Ereignisobjekts, wenn es nicht in der Liste ist if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Senden einer Nachricht über das Ereignis und setzen des Wertes des letzten Handelsereignisses event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- Wenn es das Ereignis bereits in der Liste gibt, wird das neue Objekt entfernt und eine Debugging-Nachricht angezeigt else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //+------------------------------------------------------------------+
Die Handhabung von Bedingungen verschiedener Modifikationsarten ist relativ einfach und wird in den Codekommentaren beschrieben. Abhängig von der Art der Order-/Positionsänderung wird der Ereigniscode mit Hilfe einer Reihe von Flags erstellt. Der Code wird beim Anlegen eines neuen Modifikationsereignisses an den Klassenkonstruktor CEventModify gesendet.
Die farbig markierten Codeblöcke zum Speichern neuer Order-/Positionseigenschaften werden allen Methoden zum Speichern der Klassenpositions-/Ordereigenschaften hinzugefügt. Wir werden hier nicht auf sie eingehen, da ihre Codezeichenketten identisch sind. Sie finden sie in den untenstehenden angehängten Dateien.
Jetzt ist alles bereit, um Ereignisse zur Änderung bestehender Aufträge und Positionen zu testen.
Testen der Änderung von Orders und Positionen
Um den Test durchzuführen, müssen wir den bestehenden Satz der Test-EA-Schaltflächen aus dem siebten
Artikel ergänzen.
Fügen wir noch drei weitere Schaltflächen hinzu, inklusive der Aktionen beim Drücken: StopLoss
setzen, TakeProfit setzen und Trailing aller.
Die ersten beiden Schaltflächen setzen StopLoss und
TakeProfit für alle Orders und Positionen, die keinen haben, während die dritte Schaltfläche zwei Zustände hat — Aktiviert/deaktiviert,
d.h. beim Drücken bleibt die Schaltfläche gedrückt und die beiden Trailing-Funktionen beginnen zu arbeiten. Infolgedessen beginnt der
EA mit dem Trailing-Stop aller Positionen und bewegt alle aktiven Pending-Orders, während er dem Preis folgt. Durch erneutes Drücken der
Taste wird für beide das Trailing deaktiviert.
Nehmen wir TestDoEasyPart07.mq5 EA von \MQL5\Experts\TestDoEasy\Part07 und speichern wir ihn im neuen Ordner \MQL5\Experts\TestDoEasy\Part08 unter dem Namen TestDoEasyPart08.mq5.
Wir fügen drei neue Konstanten zur Enumeration der Schaltflächen hinzu und ändere die Gesamtzahl der Schaltflächen von 17 auf 20 in der entsprechenden Makrosubstitution:
//--- 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, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20)
Fügen wir noch die Variablen hinzu, um den Abstand des StopLoss vom Preis,
die Schrittweite der Trailing-Stops, die
Anzahl der Profitpunkte anzugeben, um mit dem Trailing zu beginnen, sowie StopLoss
und TakeProfit in Punkten, die durch Anklicken der entsprechenden
Schaltflächen (die Parameter InpStopLoss und InpTakeProfit werden verwendet, um die Stopps unmittelbar nach dem Öffnen/Platzieren
einer Pending-Order einzustellen) zu den Eingaben.
Fügen wir die notwendigen Variablen zum Speichern der Werte neu hinzugefügter
Eingaben und die Flag-Variable hinzu, die die Aktivität
von Trailing-Funktion anzeigt, um die Liste der globalen Variablen zu erhalten:
//--- Eingabeparameter input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) //--- 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; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; //+------------------------------------------------------------------+
Da es sich um einen Test-EA handelt, wird die Programmoperation beim Debuggen der Bibliothek oft mit einem kritischen Fehler abgeschlossen. In diesen Fällen bleiben alle grafischen Objekte (Schaltflächen) auf dem Chart erhalten. Nachdem der Fehler behoben und der EA neu gestartet wurde, kann er die Tasten nicht mehr neu zeichnen. Er muss erneut gestartet werden, damit er zuerst die vorhandenen Schaltflächen aus dem Chart durch die Funktion OnDeinit() entfernen kann, damit er beim nächsten Start alle Schaltflächen eines leeren Charts neu zeichnen kann.
Fügen wir das Prüfen des Vorhandenseins der Schaltflächen auf dem Chart
zu OnInit() hinzu,
setzen die Werte für die Variablen der Trailing-Funktionen und
die
Stopps, checken
das Aktivitätsflag der Schaltfläche für das Trailing und aktivieren die Schaltfläche, wenn das Flag nach dem Plotten aller Schaltflächen
gesetzt ist.
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- check for undeleted objects if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- 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; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; //--- Erstellen der Schaltflächen if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- set button trailing ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Setzen der Handelsparameter trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Schreiben wir die Funktion, um das Vorhandensein eines grafischen
Objekts mit dem angegebenen Präfix auf dem Chart und die Funktion zum
Verfolgen des Status der Schaltflächen zu definieren. Für mehr Komfort beim Lesen des Codes werden wir das Verfolgen aus der
Funktion OnTick() des EA in eine separate Funktion verschieben:
//+------------------------------------------------------------------+ //| Return the flag of a prefixed object presence | //+------------------------------------------------------------------+ bool IsPresentObects(const string object_prefix) { for(int i=ObjectsTotal(0)-1;i>=0;i--) if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE) return true; return false; } //+------------------------------------------------------------------+ //| Tracking the buttons' status | //+------------------------------------------------------------------+ void PressButtonsControl(void) { 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); } } //+------------------------------------------------------------------+
Ändern wir die Funktion zum Setzen des Objektstatus der Schaltfläche:
//+------------------------------------------------------------------+ //| Set the button status | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); if(name==butt_data[TOTAL_BUTT-1].name) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+
Hier:
Setzen des Status der Schaltfläche
(aktiviert/deaktiviert),
wenn dies die allerletzte Schaltfläche ist und
wenn sie "aktiviert" ist, ändern wir die Hintergrundfarbe der Schaltfläche,
andernfalls, setzen wir die Hintergrundfarbe auf den Status "deaktiviert" zurück.
Da wir drei neue Schaltflächen haben, fügen
wir die Namen der neuen Schaltflächenobjekte entsprechend der Funktion hinzu:
//+------------------------------------------------------------------+ //| Konvertieren der Enumeration in den Text der Schaltflächen | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"set_take_profit","Set TakeProfit"); StringReplace(txt,"set_stop_loss","Set StopLoss"); StringReplace(txt,"trailing_all","Trailing All"); 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; } //+------------------------------------------------------------------+
Jetzt müssen wir das Drücken der drei neuen Tasten erfassen. Um dies zu erreichen, fügen wir die folgenden Zeilen am Ende der Tastendruck-Handhabungsfunktion PressButtonEvents() hinzu (nach dem Codeblock, der das Drücken der Schaltfläche für die Auszahlung handhabt):
//--- 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); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- Warten für 1/10 einer Sekunde Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
Wie wir sehen können, werden hier die beiden neuen Funktionen aufgerufen: SetStopLoss() und SetTakeProfit(). Damit können die entsprechenden Order- und Positionspreise einstellen:
//+------------------------------------------------------------------+ //| Set StopLoss to all orders and positions | //+------------------------------------------------------------------+ void SetStopLoss(void) { if(stoploss_to_modify==0) return; //--- Set StopLoss to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify); trade.PositionModify(position.Ticket(),sl,position.TakeProfit()); } //--- Set StopLoss to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Set TakeProfit to all orders and positions | //+------------------------------------------------------------------+ void SetTakeProfit(void) { if(takeprofit_to_modify==0) return; //--- Set TakeProfit to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify); trade.PositionModify(position.Ticket(),position.StopLoss(),tp); } //--- Set TakeProfit to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+
Die Funktionen sind recht einfach. Werfen wir einen Blick auf die Platzierung von TakeProfit für alle Aufträge und Positionen, die noch
keinen haben:
Zuerst überprüfen wir, ob die StopLoss in Punkten gesetzt werden sollen. Wenn der Wert
Null ist, kehren wir sofort zurück, da hier nichts zu ändern ist.
Als nächstes erhalten wir die
Liste ausschließlich der aktiven Marktpositionen und sortieren sie nach
TakeProfit gleich Null, da die Position keinen TakeProfit haben.
Als nächstes verwenden wir
eine Schleife über die endgültige Liste, um die Positionen zu erhalten,
wir berechnen für jede mit Hilfe der Servicefunktion, die wir im vierten
Teil der Bibliotheksbeschreibung beschrieben haben, und senden sie an
die Methode der Positionsänderungen der Standardbibliothek CTrade-Klasse.
Um TakeProfit der Orders einzustellen, erhalten wir die Liste der aktiven Pending-Orders und führen die oben beschriebenen Aktionen
durch.
Jetzt müssen wir nur noch die Funktionen zum Trailing der Stopps der Positionen und die Preise der Platzierten Orders schreiben:
//+------------------------------------------------------------------+ //| Trailing stop of a position with the maximum profit | //+------------------------------------------------------------------+ void TrailingPositions(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste CArrayObj* list_buy=CSelect::ByOrderProperty(list,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_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.bid-trailing_stop,Digits()); //--- If the price and the StopLevel based on it are higher than the new StopLoss (the distance by StopLevel is maintained) if(tick.bid-stop_level>sl) { //--- If the new StopLoss level exceeds the trailing step based on the current StopLoss if(buy.StopLoss()+trailing_step<sl) { //--- If we trail at any profit or position profit in points exceeds the trailing start, modify StopLoss if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start) trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit()); } } } } //--- Auswählen von nur Verkaufspositionen aus der Liste CArrayObj* list_sell=CSelect::ByOrderProperty(list,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); //--- Get Sell position index with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.ask+trailing_stop,Digits()); //--- If the price and StopLevel based on it are below the new StopLoss (the distance by StopLevel is maintained) if(tick.ask+stop_level<sl) { //--- If the new StopLoss level is below the trailing step based on the current StopLoss or a position has no StopLoss if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0) { //--- If we trail at any profit or position profit in points exceeds the trailing start value, modify StopLoss if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start) trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit()); } } } } } //+------------------------------------------------------------------+ //| Trailing the farthest pending orders | //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Get the list of all placed orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only Buy orders from the list CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- Sort the list by distance from the price in points (by profit in points) list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Buy order with the greatest distance int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- If the order is below the price (BuyLimit) and it should be "elevated" following the price if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on Ask order price (the distance by StopLevel is maintained) if(price<tick.ask-stop_level) { //--- If the calculated price exceeds the trailing step based on the order placement price, modify the order price if(price>buy.PriceOpen()+trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit()); } } } //--- If the order exceeds the price (BuyStop and BuyStopLimit), and it should be "decreased" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel based on Ask order price (the distance by StopLevel is maintained) if(price>tick.ask+stop_level) { //--- If the calculated price is lower than the trailing step based on order price, modify the order price if(price<buy.PriceOpen()-trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0)); } } } } } //--- Select only Sell order from the list CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- Sort the list by the distance from the price in points (by profit in points) list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Sell order having the greatest distance int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- If the order exceeds the price (SellLimit), and it needs to be "decreased" following the price if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price>tick.bid+stop_level) { //--- If the calculated price is below the trailing step based on the order price, modify the order price if(price<sell.PriceOpen()-trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit()); } } } //--- If the order is below the price (SellStop and SellStopLimit), and it should be "elevated" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price<tick.bid-stop_level) { //--- If the calculated price exceeds the trailing step based on the order price, modify the order price if(price>sell.PriceOpen()+trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0)); } } } } } } //+------------------------------------------------------------------+
Die Funktionen enthalten nichts Neues. Alle notwendigen Aktionen werden direkt in den Codekommentaren beschrieben. Ich glaube, Sie werden den Code ohne große Schwierigkeiten selbst studieren können.
Da wir nun drei weitere Schaltflächen haben, wurde die Berechnung der Koordinaten der Schaltflächen in der Funktion zur Erstellung von
Schaltflächen angepasst (siehe endgültige Liste).
Aufrufen aller Trailing-Funktionen in OnTick():
//+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- Initialize the last trading event static ENUM_TRADE_EVENT last_event=WRONG_VALUE; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); PressButtonsControl(); } //--- If the last trading event changed if(engine.LastTradeEvent()!=last_event) { last_event=engine.LastTradeEvent(); } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); TrailingOrders(); } } //+------------------------------------------------------------------+
Der vollständige Code des Tests-EAs:
//+------------------------------------------------------------------+ //| TestDoEasyPart08.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, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- Strukturen struct SDataButt { string name; string text; }; //--- Eingabeparameter input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) //--- 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; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- check for undeleted objects if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- 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; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; //--- Erstellen der Schaltflächen if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- set button trailing ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- 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() { //--- Initialize the last trading event static ENUM_TRADE_EVENT last_event=WRONG_VALUE; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); PressButtonsControl(); } //--- If the last trading event changed if(engine.LastTradeEvent()!=last_event) { last_event=engine.LastTradeEvent(); } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); TrailingOrders(); } } //+------------------------------------------------------------------+ //| Timer Funktion | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| Funktion der Chart-Ereignisse | //+------------------------------------------------------------------+ 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); } } //+------------------------------------------------------------------+ //| Return the flag of a prefixed object presence | //+------------------------------------------------------------------+ bool IsPresentObects(const string object_prefix) { for(int i=ObjectsTotal(0)-1;i>=0;i--) if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE) return true; return false; } //+------------------------------------------------------------------+ //| Tracking the buttons' status | //+------------------------------------------------------------------+ void PressButtonsControl(void) { 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); } } //+------------------------------------------------------------------+ //| Create the button panel | //+------------------------------------------------------------------+ 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)+3*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-6) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? 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); if(name==butt_data[TOTAL_BUTT-1].name) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+ //| 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,"set_take_profit","Set TakeProfit"); StringReplace(txt,"set_stop_loss","Set StopLoss"); StringReplace(txt,"trailing_all","Trailing All"); 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: Platzieren von BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Abrufen 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 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 der korrekten BuyStop-Order 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 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 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 der Kaufposition 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 if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Sell(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 if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Buy(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); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- Warten für 1/10 einer Sekunde Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Set StopLoss to all orders and positions | //+------------------------------------------------------------------+ void SetStopLoss(void) { if(stoploss_to_modify==0) return; //--- Set StopLoss to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify); trade.PositionModify(position.Ticket(),sl,position.TakeProfit()); } //--- Set StopLoss to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Set TakeProfit to all orders and positions | //+------------------------------------------------------------------+ void SetTakeProfit(void) { if(takeprofit_to_modify==0) return; //--- Set TakeProfit to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify); trade.PositionModify(position.Ticket(),position.StopLoss(),tp); } //--- Set TakeProfit to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Trailing stop of a position with the maximum profit | //+------------------------------------------------------------------+ void TrailingPositions(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste CArrayObj* list_buy=CSelect::ByOrderProperty(list,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_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.bid-trailing_stop,Digits()); //--- If the price and the StopLevel based on it are higher than the new StopLoss (the distance by StopLevel is maintained) if(tick.bid-stop_level>sl) { //--- If the new StopLoss level exceeds the trailing step based on the current StopLoss if(buy.StopLoss()+trailing_step<sl) { //--- If we trail at any profit or position profit in points exceeds the trailing start, modify StopLoss if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start) trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit()); } } } } //--- Auswählen von nur Verkaufspositionen aus der Liste CArrayObj* list_sell=CSelect::ByOrderProperty(list,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); //--- Get Sell position index with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.ask+trailing_stop,Digits()); //--- If the price and StopLevel based on it are below the new (the distance by StopLevel is maintained) if(tick.ask+stop_level<sl) { //--- If the new StopLoss level is below the trailing step based on the current StopLoss or a position has no StopLoss if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0) { //--- If we trail at any profit or position profit in points exceeds the trailing start value, modify StopLoss if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start) trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit()); } } } } } //+------------------------------------------------------------------+ //| Trailing the farthest pending orders | //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Get the list of all placed orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only Buy orders from the list CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- Sort the list by distance from the price in points (by profit in points) list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Buy order with the greatest distance int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- If the order is below the price (BuyLimit) and it should be "elevated" following the price if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on Ask order price (the distance by StopLevel is maintained) if(price<tick.ask-stop_level) { //--- If the calculated price exceeds the trailing step based on the order placement price, modify the order price if(price>buy.PriceOpen()+trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit()); } } } //--- If the order exceeds the price (BuyStop and BuyStopLimit), and it should be "decreased" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel based on Ask order price (the distance by StopLevel is maintained) if(price>tick.ask+stop_level) { //--- If the calculated price is lower than the trailing step based on order price, modify the order price if(price<buy.PriceOpen()-trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0)); } } } } } //--- Select only Sell order from the list CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- Sort the list by the distance from the price in points (by profit in points) list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Sell order having the greatest distance int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- If the order exceeds the price (SellLimit), and it needs to be "decreased" following the price if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price>tick.bid+stop_level) { //--- If the calculated price is below the trailing step based on the order price, modify the order price if(price<sell.PriceOpen()-trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit()); } } } //--- If the order is below the price (SellStop and SellStopLimit), and it should be "elevated" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price<tick.bid-stop_level) { //--- If the calculated price exceeds the trailing step based on the order price, modify the order price if(price>sell.PriceOpen()+trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0)); } } } } } } //+------------------------------------------------------------------+
Kompilieren wir den EA.
Wir setzen die Werte von StopLoss in Punkten und TakeProfit in Punkten auf
Null, um Positionen zu öffnen und Pending-Orders ohne Stopps zu platzieren. Setzen wie
StopLoss für Modifikation (Punkte) und TakeProfit für Modifikation (Punkte) auf 20 und 60
(Standardwerte) — diese StopLoss und TakeProfit werden durch Drücken der Schaltflächen eingestellt.
Starten wir den EA im Tester und setzen Pending-Orders. Dann drücken wir die Tasten zum Einstellen von StopLoss und TakeProfit
nacheinander. Die Preise sind eingestellt und die entsprechenden Einträge erscheinen im Journal. Als Nächstes aktivieren wir das
Trailing und beobachten die Orders, während sie dem Preis folgen und die entsprechenden Einträge im Journal erscheinen. Positionen, die
durch Orders ausgelöst werden, werden mit ihrem StopLoss verfolgt, und die entsprechenden Einträge erscheinen im Journal.
Netting-Konten:
Hedging-Konten:
Was kommt als Nächstes?
In den kommenden Artikeln werden wir die Bibliothek erweitern und ihre Kompatibilität mit MQL4 implementieren. Weitere spannende Dinge stehen noch bevor.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie herunterladen und
testen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.
Frühere Artikel dieser Serie:
Teil 1. Konzept, Datenverwaltung.
Teil
2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung
(Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4.
Handelsereignisse. Konzept.
Teil 5. Klassen und Kollektionen von
Handelsereignissen. Senden von Ereignissen an das Programm.
Teil 6. Ereignisse
auf Netting-Konten.
Teil 7. Ereignis der Aktivierung einer StopLimit-Order,
Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/6595





- 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.