Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIV): Handelsklassen - automatische Korrektur ungültiger Parametern

24 Januar 2020, 10:08
Artyom Trishkin
0
185

Inhalt

Im vorherigen Artikel haben wir die Kontrolle ungültiger Parameter zur Handelsklasse hinzugefügt. Die Gültigkeit der an die Handelsmethoden übergebenen Werte wird geprüft. Wenn sich einer der Parameter als ungültig herausstellt, wird bei der Rückkehr von der Handelsmethode der entsprechenden Fehler gemeldet. Dieses Verhalten erlaubt es dem EA nicht, einen Handelsserver mit absichtlich ungültigen Aufträgen zu überlasten. Auf der anderen Seite gibt es keine volle Kontrolle über das Verhalten des EA. Stattdessen können wir prüfen, ob ungültige Werte korrigiert werden können. Wenn ja, dann wäre es vernünftig, sie zu korrigieren und die angepasste Handelsorder an den Server zu senden.

Im Allgemeinen sollte der EA in der Lage sein, den Umständen entsprechend zu handeln und gleichzeitig der benutzerdefinierten Logik der Behandlung von Fehlern in Handelsaufträgen zu folgen. Daher können wir dem EA die folgenden Anweisungen geben, wenn ein Handelsauftragsfehler entdeckt wird:

  1. Einfach die Handelsmethode beenden, um den Nutzer die Behandlung für ungültige Parameter eines fehlerhaften Auftrags selbst erstellen lassen.
  2. Wenn ein ungültiger Wert eines Handelsauftrags behoben werden kann, tun Sie dies sofort und senden Sie ihn danach an den Server.
  3. Wenn ein Fehler situationsbedingt ist, wiederholen Sie eine Handelsanfrage nach einer Pause oder wiederholen Sie einfach die Anfrage mit den gleichen Parametern.

Die Behandlung von Fehlern in den Parametern der Handelsaufträge kann zu einem von mehreren Ergebnissen führen:

  • Die Unfähigkeit, den Handel mit dem EA fortzusetzen, bevor eine Fehlerquelle vom Nutzer beseitigt ist.
  • Unfähigkeit, einen Handelsauftrag zu senden — Ausstieg aus der Handelsmethode.
  • Korrigieren ungültiger Werte und Senden eines korrigierten Handelsauftrags.
  • Sofortiges Senden eines Handelsauftrags mit den Ausgangsparametern (hier wird davon ausgegangen, dass sich die Handelsbedingungen verbessert haben).
  • Warten, Aktualisieren der Kursdaten und Senden eines Handelsauftrags mit den Ausgangsparametern.

In diesem Artikel werden wir den Fehlerbehandler für Handelsaufträge entwickeln, der Fehler und ihre Quellen prüft und die Methode der Fehlerbehandlung zurückgibt:

  • deaktivieren des Handels,
  • unterbrechen des Handels,
  • korrigieren ungültiger Parameter,
  • Handelsanfrage mit den Ausgangsparametern,
  • Handelsanfrage nach einer Pause (vorläufige Lösung),
  • Erstellen einer schwebenden (pending) Handelsanfrage (in nachfolgenden Artikeln)

Konzept

Das Deaktivieren des Handels ist notwendig, wenn der Handel auf dem Server entweder vollständig deaktiviert ist oder wenn die EAs die Handelsanfragen nutzlos machen. In diesem Fall kann der EA nur als analytischer Assistent verwendet werden. Um dies zu erreichen, benötigen wir das globale Flag, das beim ersten Handelsversuch gesetzt werden soll und das die Unmöglichkeit des Handels bestimmt.

Unterbrechung eines Handelsvorgangs: Im Falle eines Fehlers ist die Handelsmethode zu beenden, um dem Nutzer die Möglichkeit zu geben, die Handelsversuche mit den gleichen Parametern fortzusetzen.

Die Korrektur ungültiger Parameter funktioniert wie folgt: Bei der Überprüfung der Gültigkeit der Handelsanfrage erstellen wir eine Liste aller festgestellten Fehler. Die Methode zur Überprüfung der Parameter sieht alle Fehler aus der Liste durch und gibt den Verhaltenscode der Handelsmethode zurück. Wenn ein Fehler den weiteren Handel unmöglich macht, gibt die Methode den Code zum Verlassen der Handelsmethode zurück, da das Senden eines Handelsauftrags immer noch kein positives Ergebnis liefert. Wenn der Fehler behoben werden kann, werden die Methoden zur Korrektur der entsprechenden Handelsauftragswerte aufgerufen und das Ergebnis der erfolgreichen Überprüfung zurückgegeben. Außerdem gibt die Methode die Verhaltenscodes "Warten und wiederholen", "Daten aktualisieren und wiederholen" und "Eine schwebende Anfrage erstellen" der Handelsmethode zurück.

Was bedeutet das?

Das Verhalten von "Warten und wiederholen" kann notwendig sein, wenn sich der Markt nahe an einem der Stopp-Preise des Auftrags oder ihrem Aktivierungspreis befindet, während wir versuchen, den Stopp-Preis zu ändern oder einen Auftrag zu entfernen bzw. eine Position zu schließen. Wenn sich der Aktivierungspreis des Stopps innerhalb des Einfrierens der Handelsoperationen befindet, gibt der Server das Verbot zur Änderung des Auftragswertes zurück. In diesem Fall gibt es nur eine Lösung — einfach eine Weile warten und hoffen, dass der Marktpreis den Bereich verlässt. Danach senden Sie eine Handelsanfrage mit geänderten Parametern der Order/Position oder die Order wird nach dem Warten entfernt.
Das Verhalten "Daten aktualisieren und wiederholen" kann notwendig sein, wenn die Preise veraltet sind und wir während der Bearbeitung eines Handelsauftrags ein "Requote" erhalten haben.

Das Verhalten "Erstellen einer schwebenden Anfrage". Was bedeutet das?
Wenn wir uns die beiden vorhergehenden Abwicklungsmethoden genau ansehen, wird klar, dass wir bei der Handelsmethode beim Warten einfach warten, bis die Wartezeit vorbei ist. Ein solches Verhalten ist gerechtfertigt, wenn wir während des Wartens nicht das Handelsumfeld analysieren müssen. Um das Programm von der Notwendigkeit zu befreien, innerhalb der Handelsmethode "stehen" zu bleiben, erstellen wir einfach eine schwebende Handelsanfrage, die die notwendigen Parameter und die Wartezeit mit der Anzahl der Wiederholungen enthält.

Die Erstellung einer schwebenden Anfrage macht somit die Notwendigkeit des Verhaltens "Daten aktualisieren und wiederholen" und "Warten und wiederholen" vollständig überflüssig. Diese beiden Verhaltensweisen sind im Wesentlichen ausstehende Handelsanforderungen (mit den minimalen und spezifizierten Wartezeiten). Außerdem bietet die Möglichkeit, schwebende Anfragen im Programm zu stellen, den Nutzern eine weitere Methode zur Durchführung von Handelsoperationen. Wir werden die Implementierung der schwebenden Anforderungen für nachfolgende Artikel belassen.

Bevor wir anfangen, möchte ich Sie daran erinnern, dass wir begonnen haben, Änderungen in der Definition des Handelsereignisses im vorherigen Artikel vorzunehmen:

Ich habe mehrere Nutzerberichte über den Fehler erhalten, der beim Empfang des letzten Handelsereignisses festgestellt wurde. Der Test EA, der sich auf die Artikel bezieht und beschreibt, wie man Handelsereignisse empfängt, erhält Daten über das aufgetretene Handelsereignis, indem er den Wert des vorherigen Ereignisses mit dem aktuellen vergleicht. Dies wäre für den Zweck des Tests der Verfolgung von Handelsereignissen durch die Bibliothek ausreichend, da ich nicht beabsichtigte, die unfertige Version der Bibliothek in benutzerdefinierten Anwendungen zu verwenden, wenn ich Artikel über Handelsereignisse schreibe. Es stellte sich jedoch heraus, dass die Beschaffung von Informationen über Trading-Events sehr gefragt ist und es wichtig ist, das letzte aufgetretene Ereignis genau zu kennen.

Die implementierte Methode zum Erhalten eines Handelsereignisses kann einige Ereignisse überspringen. Zum Beispiel, wenn Sie eine Pending Order zweimal hintereinander setzen, wird die zweite nicht im Programm verfolgt (die Bibliothek verfolgt alle Ereignisse), da sie mit der vorletzten übereinstimmt ("Platzieren einer Pending-Order"), obwohl die Orders selbst sich tatsächlich unterscheiden können.

Deshalb werden wir dieses Verhalten korrigieren. Heute werden wir ein einfaches Flag implementieren, das das Programm über ein Ereignis informiert, so dass wir in der Lage sind, Daten über das Ereignis im Programm anzuzeigen. Im nächsten Artikel werden wir das Erhalten von Handelsereignissen im Programm vervollständigen, indem wir eine vollständige Liste aller gleichzeitig aufgetretenen Ereignisse erstellen und sie an das Programm senden. Auf diese Weise können wir nicht nur die Information über das eingetretene Handelsereignis erhalten, sondern auch alle gleichzeitig eingetretenen Ereignisse ansehen, wie es für Konto und Ergebnissee der Symbolkollektion geschieht.

Lassen Sie uns also die Arbeit an der Änderung dieser Funktionalität abschließen, bevor wir unsere Arbeit an der Handelsklasse wieder aufnehmen.

Korrektur der Definition der Handelsereignisse

Da alle unsere Objekte tatsächlich auf dem Basisobjekt aller Bibliotheksobjekte basieren, das die Liste der Ereignisse und die Methode, die das Ereignisflag des Objekts zurückgibt, enthält, fügen wir alle Handelsereignisse zur Ereignisliste des Basisobjekts hinzu, während das Ereignisflag von der Klasse mit der Methode IsEvent() erhalten werden kann. Ereignisflags werden automatisch von der Klasse gesetzt. Wir sollten jedoch in der Lage sein, das Flag eines eingetretenen Handelsereignisses von anderen Klassen und ihren Ereignisbehandlern zu setzen.
Um dies zu tun, fügen Sie der Klasse CEventBaseObj in der Datei BaseObj.mqh die Methode zum Setzen des Basisobjekt-Ereignisflags hinzu:

//--- Set/return the occurred event flag to the object data
   void              SetEvent(const bool flag)                       { this.m_is_event=flag;                   }
   bool              IsEvent(void)                             const { return this.m_is_event;                 }


Wenn nun ein neues Ereignis in der Klasse der Handelsereignisse CEventsCollection auftritt, müssen wir die Ereignisbeschreibung erstellen, sie in die Liste der neuen Ereignisse der Basisklasse aller Objekte aufnehmen und das neue Ereignis-Flag setzen.

Auf diese Weise werden die Beschreibungen aller neu aufgetretenen Ereignisse in die Liste der Handelsereignisse der Basisklasse aller Objekte der Symbolsammlung aufgenommen. Von dieser Liste aus können wir die Liste im Programm leicht lesen und jedes Ereignis in ihr behandeln.

Lassen Sie uns alle notwendigen Verbesserungen an der Datei der Handelsereignisse EventsCollection.mqh vornehmen.

Hinzufügen der Definition der beiden neuen Methoden zum 'public' Teil der Klasse
die Methode zum Empfangen des Basis-Ereignisobjekts durch seinen Index in der Liste
und
die Methode, die die Anzahl der neuen Ereignisse zurückgibt:

//--- Return (1) the last trading event on an account, (2) base event object by index and (3) number of new events
   ENUM_TRADE_EVENT  GetLastTradeEvent(void)                const { return this.m_trade_event;                 }
   CEventBaseObj    *GetTradeEventByIndex(const int index)        { return this.GetEvent(index,false);         }
   int               GetTradeEventsTotal(void)              const { return this.m_list_events.Total();         }
//--- Rücksetzen des letzten Handelsereignisses


Die Methode, die das Basis-Ereignisobjekt nach Index zurückgibt, ruft die Methode GetEvent() auf, die den erforderlichen Ereignisindex und das Reset-Flag (false) enthält und den über die Ereignisliste hinausgehenden Index prüft, um das zurückgegebene Ereignis nicht zu korrigieren, wenn der Index über die Ereignisliste hinausgeht. Mit anderen Worten, wenn wir einen nicht existierenden Index übergeben, gibt die Methode NULL zurück. Wenn wir das Flag auf true setzen würden, würde die Methode das letzte Ereignis zurückgeben, das wir hier nicht benötigen.

Die Methode, die die Anzahl der neuen Ereignisse zurück gibt, gibt einfach die Größe der Basisobjektliste zurück.

Da die Listen der historischen und Markt-Orders und -Positionen ständig in der Klasse der Kollektion für Handelsereignisse des Timers angezeigt werden, müssen wir die Liste der Basis-Handelsereignisse löschen und die Flagge für sortierte Listen in der Refresh()-Methode setzen:

//+------------------------------------------------------------------+
//| Aktualisieren der Ereignisliste                                  |
//+------------------------------------------------------------------+
void CEventsCollection::Refresh(CArrayObj* list_history,
                                CArrayObj* list_market,
                                CArrayObj* list_changes,
                                CArrayObj* list_control,
                                const bool is_history_event,
                                const bool is_market_event,
                                const int  new_history_orders,
                                const int  new_market_pendings,
                                const int  new_market_positions,
                                const int  new_deals,
                                const double changed_volume)
  {
//--- Rückkehren, wenn die Liste leer ist
   if(list_history==NULL || list_market==NULL)
      return;
//---
   this.m_is_event=false;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
//--- Wenn das Ereignis in der Umgebung des Marktes existiert
   if(is_market_event)
     {


Nach dem Ereignis, das eine Zeichenkette sendet, müssen wir in allen Methoden zur Erstellung des neuen Ereignisses CreateNewEvent() zur Liste der Basisereignisse hinzufügen:

         event.SendEvent();
         CBaseObj::EventAdd(this.m_trade_event,order.Ticket(),order.Price(),order.Symbol());


Dies wurde bereits in den Methodenlisten festgelegt, so dass es keinen Sinn macht, hier darauf zu verweilen, so können wir für den Artikel Platz sparen. Alles ist in den angehängten Dateien zu finden.

Fügen Sie nun im 'public' Teil der Klasse des Basisobjekts der Bibliothek CEngine die Methoden hinzu, die das Basis-Ereignisobjekt über seinen Index in der Liste, und die die Anzahl der neuen Ereignisse zurückgibt:

//--- Return (1) the list of order, deal and position events, (2) base trading event object by index and the (3) number of new trading events
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
   CEventBaseObj       *GetTradeEventByIndex(const int index)           { return this.m_events.GetTradeEventByIndex(index);   }
   int                  GetTradeEventsTotal(void)                 const { return this.m_events.GetTradeEventsTotal();         }
//--- Rücksetzen des letzten Handelsereignisses


Diese Methoden rufen einfach die oben beschriebenen Methoden der Kollektionsklasse für gleichnamige Handelsereignisse auf.

Dies sind alle notwendigen Änderungen, die es Ihnen ermöglichen, alle gleichzeitig aufgetretenen Ereignisse zu verfolgen und alles auf einmal an das Programm zu senden. Dies wird später — beim Testen der im Artikel beschriebenen Funktionen — deutlich werden.

Jetzt können wir mit der weiteren Verfeinerung der Handelsklasse beginnen.

Fehlerbehandlung der Parameter der Handelsaufträge

Zuerst werden die Indizes der notwendigen Nachrichten zur Datei Datas.mqh hinzugefügt:

   MSG_LIB_TEXT_REQUEST_REJECTED_DUE,                 // Request was rejected before sending to the server due to:
   MSG_LIB_TEXT_INVALID_REQUEST,                      // Invalid request:
   MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR,                 // Insufficient funds for performing a trade

   MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ,        // Unsupported price parameter type in a request
   MSG_LIB_TEXT_TRADING_DISABLE,                      // Trading disabled for the EA until the reason is eliminated
   MSG_LIB_TEXT_TRADING_OPERATION_ABORTED,            // Trading operation is interrupted
   MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST,              // Correcting trading request parameters
   MSG_LIB_TEXT_CREATE_PENDING_REQUEST,               // Creating a pending request
   MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT,          // Unable to correct a lot
   
  };


und die den Indizes entsprechenden Texte:

   {"Запрос отклонён до отправки на сервер по причине:","Request rejected before being sent to server due to:"},
   {"Ошибочный запрос:","Invalid request:"},
   {"Недостаточно средств для совершения торговой операции","Not enough money to perform trading operation"},

   {"Неподдерживаемый тип параметра цены в запросе","Unsupported price parameter type in request"},
   {"Торговля отключена для эксперта до устранения причины запрета","Trading for the expert is disabled until this ban is eliminated"},
   {"Торговая операция прервана","Trading operation aborted"},
   {"Корректировка параметров торгового запроса ...","Correction of trade request parameters ..."},
   {"Создание отложенного запроса","Create pending request"},
   {"Нет возможности скорректировать лот","Unable to correct the lot"},
   
  };


Fügen Sie in der Datei Defines.mqh die Enumerationen hinzu, die wir benötigen, um die Art und Weise der Behandlung von Fehlern in Handelsanforderungen und vom Handelsserver zurückgegebenen Fehlern zu definieren und zurückzugeben.

Um ein Verhalten festzulegen, das aktiviert wird, wenn ein Fehler des Eas direkt empfangen wird, fügen Sie die Enumeration hinzu, die das mögliche Verhalten des EA beschreibt, wenn ein Fehler in der Handelsanfrage entdeckt wird oder wenn ein Fehler vom Handelsserver zurückgegeben wird:

//+------------------------------------------------------------------+
//| EA behavior when handling errors                                 |
//+------------------------------------------------------------------+
enum ENUM_ERROR_HANDLING_BEHAVIOR
  {
   ERROR_HANDLING_BEHAVIOR_BREAK,                           // Abort trading attempt
   ERROR_HANDLING_BEHAVIOR_CORRECT,                         // Correct invalid parameters
   ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST,                 // Create a pending request
  };
//+------------------------------------------------------------------+


Es wird möglich sein, das bevorzugte Verhalten von EA bei der Behandlung von Fehlern in den EA-Einstellungen festzulegen, indem einer der Aufzählungsparameter angegeben wird.

Verschiedene Methoden zur Fehlerbehandlung sind möglich, wenn die Werte der Handelsauftragsparameter überprüft werden. Um herauszufinden, welche Fehler bei der Überprüfung der Parameter von Handelsaufträgen erkannt werden und welche Handelsbedingungen diese Fehler beeinflussen, müssen der Enumeration mit Flags der möglichen Fehlerbehandlungsmethoden hinzufügen:

//+------------------------------------------------------------------+
//| Flags indicating the trading request error handling methods      |
//+------------------------------------------------------------------+
enum ENUM_TRADE_REQUEST_ERR_FLAGS
  {
   TRADE_REQUEST_ERR_FLAG_NO_ERROR                 =  0,    // No error
   TRADE_REQUEST_ERR_FLAG_FATAL_ERROR              =  1,    // Disable trading for an EA (critical error) - exit
   TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR             =  2,    // Library internal error - exit
   TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST            =  4,    // Error in the list - handle (ENUM_ERROR_CODE_PROCESSING_METHOD)
  };
//+------------------------------------------------------------------+


Wenn wir die Parameter von Handelsaufträgen und deren Ausführbarkeit überprüfen, fügen wir die Kennzeichen für das Fehlerbehandlungsverhalten hinzu:

  • 0 — kein Fehler, der Handelsauftrag kann gesendet werden,
  • 1 — kritischer Fehler — es macht keinen Sinn, Handelsversuche zu unternehmen, der EA sollte auf den analytischen Assistenten-Modus ohne Handelsaktivität umgeschaltet werden,
  • 2 — etwas ist schief gelaufen, und es gab einen Fehler in der Bibliothek — einfach die weitere Ausführung der Handelsmethode unterbrechen, um die Fehlfunktion der Handelsklasse zu vermeiden,
  • 4 — der Fehler kann behoben werden, und es wird in die Fehlerliste geschrieben, um die Methode zur Behebung des Fehlers aufzurufen.

Die Fehlerprüfmethode gibt die Möglichkeiten zur korrekten Behandlung der erkannten Fehler zurück.
Dazu fügen wir die Enumeration der möglichen Methoden zur Behandlung von Handelsauftragsfehlern sowie der vom Handelsserver zurückgegebenen Fehler hinzu:

//+------------------------------------------------------------------+
//| The methods of handling errors and server return codes           |
//+------------------------------------------------------------------+
enum ENUM_ERROR_CODE_PROCESSING_METHOD
  {
   ERROR_CODE_PROCESSING_METHOD_OK,                         // No errors
   ERROR_CODE_PROCESSING_METHOD_DISABLE,                    // Disable trading for the EA
   ERROR_CODE_PROCESSING_METHOD_EXIT,                       // Exit the trading method
   ERROR_CODE_PROCESSING_METHOD_REFRESH,                    // Update data and repeat
   ERROR_CODE_PROCESSING_METHOD_WAIT,                       // Wait and repeat
   ERROR_CODE_PROCESSING_METHOD_PENDING,                    // Create a pending request
  };
//+------------------------------------------------------------------+


Die Methoden der Fehlerbehandlung sind aus den Beschreibungen der Aufzählungskonstanten ersichtlich.

Verbessert wurde auch die Basis des Handelsobjekts.

Abhängig von der Art und Weise, wie die Balken auf dem Chart aufgebaut sind, wird der Handel entweder durch Ask- und Bid-Preise oder durch Ask- und Last-Preise durchgeführt. Derzeit wird in der Basis-Handelsklasse nur der Handel nach Ask- und Bid-Preisen angeordnet. Fügen wir die Möglichkeit hinzu, die Preise zu überprüfen, die zur Erstellung des Charts verwendet werden. Passen Sie auch die Preise an, die wir für den Handel verwenden werden. Außerdem bietet MQL5 die Struktur der Ergebnisse der Handelsanfrage MqlTradeResult sowie die Felder 'retcode' und 'comment', die den Fehlercode bzw. die Fehlercodebeschreibung enthalten. Dies ermöglicht die Überprüfung der Codes, die vom Handelsserver nach dem Senden einer Handelsorder an den Server zurückgegeben werden. MQL4 hat keine solche Funktion, daher sollte der Fehlercode von der Funktion GetLastError() gelesen werden, die den letzten Fehlercode zurückgibt. Da unsere Bibliothek eine Multiplattform-Bibliothek ist, müssen wir im Falle von MQL4 die Felder der Handelsanforderungsstruktur ausfüllen, nachdem wir sie an den Server gesendet haben.

Bei der Überprüfung des Abstands der Stop-Order im Verhältnis zum Preis berücksichtigen wir auch die Abstände der minimal akzeptablen Stop-Level (StopLevel), die für ein Symbol festgelegt wurden. Wenn der Wert des StopLevels, der von der Funktion SymbolInfoInteger() mit der Eigenschafts-ID SYMBOL_TRADE_STOPS_LEVEL zurückgegeben wird, gleich Null ist, bedeutet dies die Existenz eines minimalen Punktabstands für Stop-Order vom Preis. Es bedeutet nur, dass das Niveau veränderlich ist. Um also den Wert der Verschiebung des Stop-Levels vom Preis zu korrigieren, müssen wir das Level "in situ" auswählen oder den aktuellen Spread multipliziert mit einem bestimmten Wert als Verschiebungswert verwenden. Der doppelte Spread wird normalerweise für eine unproblematische Anpassung der Stop-Level verwendet. Fügen Sie den Multiplikator sowie seine Rückgabe- und Einstellungsmethoden zum Handelsobjekt hinzu, um den Multiplikator auf das Handelsobjekt jedes Symbols einstellen zu können.

Fügen wir die erforderlichen Änderungen an der Basisklasse CTradeObj des Handelsobjekts in der Datei TradeObj.mqh hinzu.

Deklarieren Sie im privaten Abschnitt der Klasse zwei Klassenvariablen zur Speicherung des Preistyps für die Konstruktion von Bars und den Spread-Multiplikator zur Anpassung der Stop-Order-Level:

   SActions                   m_datas;
   MqlTick                    m_tick;                                            // Tick structure for receiving prices
   MqlTradeRequest            m_request;                                         // Trade request structure
   MqlTradeResult             m_result;                                          // trade request execution result
   ENUM_SYMBOL_CHART_MODE     m_chart_mode;                                      // Price type for constructing bars
   ENUM_ACCOUNT_MARGIN_MODE   m_margin_mode;                                     // Margin calculation mode
   ENUM_ORDER_TYPE_FILLING    m_type_filling;                                    // Filling policy
   ENUM_ORDER_TYPE_TIME       m_type_expiration;                                 // Order expiration type
   int                        m_symbol_expiration_flags;                         // Flags of order expiration modes for a trading object symbol
   ulong                      m_magic;                                           // Magic number
   string                     m_symbol;                                          // Symbol
   string                     m_comment;                                         // Comment
   ulong                      m_deviation;                                       // Slippage in points
   double                     m_volume;                                          // Volume
   datetime                   m_expiration;                                      // Order expiration time (for ORDER_TIME_SPECIFIED type order)
   bool                       m_async_mode;                                      // Flag of asynchronous sending of a trade request
   ENUM_LOG_LEVEL             m_log_level;                                       // Logging level
   int                        m_stop_limit;                                      // Distance of placing a StopLimit order in points
   bool                       m_use_sound;                                       // The flag of using sounds of the object trading events
   uint                       m_multiplier;                                      // The spread multiplier to adjust levels relative to StopLevel
   


Fügen wir im 'public' Teil der Klasse die Methode zur Einstellung des Spread-Multiplikators und die Methode zur Rückgabe des Multiplikatorwertes hinzu:

public:
//--- Konstructor
                              CTradeObj();

//--- Set/return the spread multiplier
   void                       SetSpreadMultiplier(const uint value)        { this.m_multiplier=(value==0 ? 1 : value);  }
   uint                       SpreadMultiplier(void)                 const { return this.m_multiplier;                  }
//--- Set default values


Prüfen wir beim Einstellen des Spread-Multiplikators, ob der an die Methode übergebene Wert gleich Null ist. Wenn ja, weisen wir den Wert 1 zu.

Fügen wir außerdem im 'public' Teil der Klasse zwei Methoden hinzu — eine, die den Fehlercode der Handelsanfrage und eine, die die Fehlercodebeschreibung der Handelsanfrage festlegt:

//--- Set the error code in the last request result
   void                       SetResultRetcode(const uint retcode)                     { this.m_result.retcode=retcode;       }
   void                       SetResultComment(const string comment)                   { this.m_result.comment=comment;       }
//--- Data on the last request result:


Im Klassenkonstruktor weisen wir dem Spread-Multiplikator den Standardwert 1 zu:

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CTradeObj::CTradeObj(void) : m_magic(0),
                             m_deviation(5),
                             m_stop_limit(0),
                             m_expiration(0),
                             m_async_mode(false),
                             m_type_filling(ORDER_FILLING_FOK),
                             m_type_expiration(ORDER_TIME_GTC),
                             m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"),
                             m_log_level(LOG_LEVEL_ERROR_MSG)
  {
   //--- Margin calculation mode
   this.m_margin_mode=
     (
      #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE)
      #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif 
     );
   //--- Spread multiplier
   this.m_multiplier=1;
   //--- Set default sounds and flags of using sounds
   this.m_use_sound=false;
   this.InitSounds();
  }
//+------------------------------------------------------------------+


In der Methode Init(), die die Standardwerte der Parameter des Handelsobjekts definiert, setzen wir den Variablenwert m_chart_mode, der die Art der Preisdarstellung speichert:

//+------------------------------------------------------------------+
//| Set default values                                               |
//+------------------------------------------------------------------+
void CTradeObj::Init(const string symbol,
                     const ulong magic,
                     const double volume,
                     const ulong deviation,
                     const int stoplimit,
                     const datetime expiration,
                     const bool async_mode,
                     const ENUM_ORDER_TYPE_FILLING type_filling,
                     const ENUM_ORDER_TYPE_TIME type_expiration,
                     ENUM_LOG_LEVEL log_level)
  {
   this.SetSymbol(symbol);
   this.SetMagic(magic);
   this.SetDeviation(deviation);
   this.SetVolume(volume);
   this.SetExpiration(expiration);
   this.SetTypeFilling(type_filling);
   this.SetTypeExpiration(type_expiration);
   this.SetAsyncMode(async_mode);
   this.SetLogLevel(log_level);
   this.m_symbol_expiration_flags=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_EXPIRATION_MODE);
   this.m_volume=::SymbolInfoDouble(this.m_symbol,SYMBOL_VOLUME_MIN);
   this.m_chart_mode=#ifdef __MQL5__ (ENUM_SYMBOL_CHART_MODE)::SymbolInfoInteger(this.m_symbol,SYMBOL_CHART_MODE) #else SYMBOL_CHART_MODE_BID #endif ;
  }
//+------------------------------------------------------------------+


Hier erhalten wir für MQL5 die Daten mit der Funktion SymbolInfoInteger() mit der ID SYMBOL_CHART_MODE, während im Falle von MQL4 die Bars durch Bid-Preise konstruiert werden.

Nun sollten wir das Eintragen der Rückgabestruktur des Handelsserver zu jeder Handelsmethode hinzufügen.
Nutzen wir die Methode der Positionsöffnung als Beispiel:

//+------------------------------------------------------------------+
//| Open a position                                                  |
//+------------------------------------------------------------------+
bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type,
                             const double volume,
                             const double sl=0,
                             const double tp=0,
                             const ulong magic=ULONG_MAX,
                             const string comment=NULL,
                             const ulong deviation=ULONG_MAX)
  {
   ::ResetLastError();
   //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false'
   if(!::SymbolInfoTick(this.m_symbol,this.m_tick))
     {
      this.m_result.retcode=::GetLastError();
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode));
      return false;
     }
   //--- Clear the structures
   ::ZeroMemory(this.m_request);
   ::ZeroMemory(this.m_result);
   //--- Fill in the request structure
   this.m_request.action   =  TRADE_ACTION_DEAL;
   this.m_request.symbol   =  this.m_symbol;
   this.m_request.magic    =  (magic==ULONG_MAX ? this.m_magic : magic);
   this.m_request.type     =  OrderTypeByPositionType(type);
   this.m_request.price    =  (type==POSITION_TYPE_BUY ? this.m_tick.ask : (this.m_chart_mode==SYMBOL_CHART_MODE_BID ? this.m_tick.bid : this.m_tick.last));
   this.m_request.volume   =  volume;
   this.m_request.sl       =  sl;
   this.m_request.tp       =  tp;
   this.m_request.deviation=  (deviation==ULONG_MAX ? this.m_deviation : deviation);
   this.m_request.comment  =  (comment==NULL ? this.m_comment : comment);
   //--- Return the result of sending a request to the server
#ifdef __MQL5__
   return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result));
#else 
   ::ResetLastError();
   int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE);
   if(ticket!=WRONG_VALUE)
     {
      ::SymbolInfoTick(this.m_symbol,this.m_tick);
      this.m_result.retcode=::GetLastError();
      this.m_result.ask=this.m_tick.ask;
      this.m_result.bid=this.m_tick.bid;
      this.m_result.deal=ticket;
      this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price);
      this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume);
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      return true;
     }
   else
     {
      ::SymbolInfoTick(this.m_symbol,this.m_tick);
      this.m_result.retcode=::GetLastError();
      this.m_result.ask=this.m_tick.ask;
      this.m_result.bid=this.m_tick.bid;
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      return false;
     }
#endif 
  }
//+------------------------------------------------------------------+


Hier geben wir für MQL5 wie bisher das Ergebnis der Funktion OrderSend() zurück, während, im Falle von MQL4, die Ticketnummer überprüft wird, die von der MQL4-OrderSend-Funktion zurückgegeben wurde. Wenn eine Handelsauftrag erfolgreich ausgeführt wurde, gibt die Funktion das Ticket des Auftrags zurück. Ein Fehler erzeugt WRONG_VALUE. Stellen Sie daher sicher, dass die Funktion einen anderen Wert als -1 zurückgibt. Wenn ja, aktualisieren Sie die Symbolpreise, füllen Sie die Ergebnisstruktur der Handelsanforderung mit den entsprechenden Daten aus und geben Sie true zurück — die Funktion wurde erfolgreich ausgeführt.
Wenn die Funktion zum Senden von Aufträgen -1 zurückgibt, schreiben Sie den letzten Fehlercode , die aktuellen Preise und die letzte Fehlercodebeschreibung in die Ergebnisstruktur der Handelsanfrage. Die übrigen Strukturfelder werden gleich Null belassen. Als Ergebnis wird false für den gesendeten Handelsauftrag zurückgegeben.

Dank dieser Verfeinerung können wir das Ergebnis der Anfrage mit Hilfe der Klassenmethoden unabhängig vom Ergebnis des Sendens der Handelsorder sehen:

//--- Data on the last request result:
//--- Return (1) operation result code, (2) performed deal ticket, (3) placed order ticket,
//--- (4) deal volume confirmed by a broker, (5) deal price confirmed by a broker,
//--- (6) current market Bid (requote) price, (7) current market Ask (requote) price
//--- (8) broker comment to operation (by default, it is filled by the trade server return code description),
//--- (9) request ID set by the terminal when sending, (10) external trading system return code
   uint                       GetResultRetcode(void)                             const { return this.m_result.retcode;        }
   ulong                      GetResultDeal(void)                                const { return this.m_result.deal;           }
   ulong                      GetResultOrder(void)                               const { return this.m_result.order;          }
   double                     GetResultVolume(void)                              const { return this.m_result.volume;         }
   double                     GetResultPrice(void)                               const { return this.m_result.price;          }
   double                     GetResultBid(void)                                 const { return this.m_result.bid;            }
   double                     GetResultAsk(void)                                 const { return this.m_result.ask;            }
   string                     GetResultComment(void)                             const { return this.m_result.comment;        }
   uint                       GetResultRequestID(void)                           const { return this.m_result.request_id;     }
   uint                       GetResultRetcodeEXT(void)                          const { return this.m_result.retcode_external;}


Die übrigen Handelsmethoden werden auf ähnliche Weise abgeschlossen, daher ist es überflüssig, sie hier zu besprechen. Sie finden alles, was Sie brauchen, in den unten angehängten Dateien.

In der Kontoobjektklasse CAccount der Datei Account.mqh verbessern wir die Methode der Rückgabe der für die Eröffnung einer Position oder die Einstellung der erforderlichen Marge einer Pending-Order:

//+------------------------------------------------------------------+
//| Return the margin required for opening a position                |
//| or placing a pending order                                       |
//+------------------------------------------------------------------+
double CAccount::MarginForAction(const ENUM_ORDER_TYPE action,const string symbol,const double volume,const double price) const
  {
   double margin=EMPTY_VALUE;
   #ifdef __MQL5__
      return(!::OrderCalcMargin(ENUM_ORDER_TYPE(action%2),symbol,volume,price,margin) ? EMPTY_VALUE : margin);
   #else 
      return this.MarginFree()-::AccountFreeMarginCheck(symbol,ENUM_ORDER_TYPE(action%2),volume);
   #endif
  }
//+------------------------------------------------------------------+


Hier muss lediglich eine Konvertierung des an die Methode übergebenen Auftragstyps in zwei mögliche Werte — ORDER_TYPE_BUY oder ORDER_TYPE_SELL — vorgenommen werden, da die Funktionen MQL5 und MQL4, mit denen die Methode arbeitet, nur diesen Auftragstyp erfordern.
Wie Sie sich vielleicht erinnern, ergibt der Rest der Division der Auftragsart Konstante durch 2 immer einen der beiden Werte:

  • entweder 0 (ORDER_TYPE_BUY),
  • oder 1 (ORDER_TYPE_SELL).

Genau dies ist notwendig, um eine Konvertierung in den richtigen Auftragstyp vorzunehmen.

Wir haben bereits die nutzerdefinierte Struktur für das Ausfüllen der Preisparameter der Handelsorder in der Klasse CTrading aus der Datei Trading.mqh erstellt:

   struct SDataPrices
     {
      double            open;                // Open price
      double            limit;               // Limit order price
      double            sl;                  // StopLoss price
      double            tp;                  // TakeProfit price
     };
   SDataPrices          m_req_price;         // Trade request prices


MQL verfügt jedoch die Struktur MqlTradeRequest. Um also eine redundante Struktur zu vermeiden,
ersetzen wir die nutzerdefinierte Struktur durch die Standardstruktur im 'private' Teil der Klasse
, und wir
deklarieren die Klassenvariable zum Speichern des Flags der Fehlerquelle in der Handelsanfrage und
die Variable für die Speicherung des EA-Verhaltens bei Fehlern, die beim Senden von Handelsaufträgen
auftreten:

//+------------------------------------------------------------------+
//| Trading class                                                    |
//+------------------------------------------------------------------+
class CTrading
  {
private:
   CAccount            *m_account;                       // Pointer to the current account object
   CSymbolsCollection  *m_symbols;                       // Pointer to the symbol collection list
   CMarketCollection   *m_market;                        // Pointer to the list of the collection of market orders and positions
   CHistoryCollection  *m_history;                       // Pointer to the list of the collection of historical orders and deals
   CArrayInt            m_list_errors;                   // Error list
   bool                 m_is_trade_disable;              // Flag disabling trading
   bool                 m_use_sound;                     // The flag of using sounds of the object trading events
   ENUM_LOG_LEVEL       m_log_level;                     // Logging level
   MqlTradeRequest      m_request;                       // Trade request prices
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Flags of error source in a trading method
   ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error
//--- Add the error code to the list
   bool                 AddErrorCodeToList(const int error_code);


Schreiben Sie außerdem im 'private' Teil der Klasse die Methode, die das Flag innerhalb der Variable zurückgibt, die die Fehlerquellen-Flags schreibt,
die Methode, die das Vorhandensein des Fehlercodes in der Fehlerliste zurückgibt, und
die Methoden der Platzierung und Rückgabemaßnahmen bei der Behandlung von Fehlern :

//--- Return the flag presence in the trading event error reason
   bool                 IsPresentErrorFlag(const int code)     const { return (this.m_error_reason_flags & code)==code;                               }
//--- Return the error code in the list
   bool                 IsPresentErorCode(const int code)            { this.m_list_errors.Sort(); return this.m_list_errors.Search(code)>WRONG_VALUE; }
//--- Set/return the error handling action
   void                 SetErrorHandlingBehavior(const ENUM_ERROR_HANDLING_BEHAVIOR behavior) { this.m_err_handling_behavior=behavior;                }
   ENUM_ERROR_HANDLING_BEHAVIOR  ErrorHandlingBehavior(void)   const { return this.m_err_handling_behavior;                                           }

//--- Check trading limitations


Entfernen Sie den Code für die Anzeige der Meldung im Journal aus der Methode zur Prüfung der Mittelunterdeckung:

   if(money_free<=0 #ifdef __MQL4__ || ::GetLastError()==134 #endif )
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         //--- create a message text
         string message=
           (
            symbol_obj.Name()+" "+::DoubleToString(volume,symbol_obj.DigitsLot())+" "+
            (
             order_type>ORDER_TYPE_SELL ? OrderTypeDescription(order_type,false,false) : 
             PositionTypeDescription(PositionTypeByOrderType(order_type))
            )+" ("+::DoubleToString(money_free,(int)this.m_account.CurrencyDigits())+")"
           );
         //--- display a journal message
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR),": ",message);
         this.AddErrorCodeToList(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR);
        }
      return false;
     }


Jetzt wird die Meldung über die Mittelknappheit von einer anderen Methode angezeigt.
In der aktuellen Methode fügen Sie einfach das Flag hinzu, das die Suche nach dem Fehler in der Fehlerliste anweist, und fügen Sie den Fehlercode zur Fehlerliste hinzu:

//+------------------------------------------------------------------+
//| Check if the funds are sufficient                                |
//+------------------------------------------------------------------+
bool CTrading::CheckMoneyFree(const double volume,
                              const double price,
                              const ENUM_ORDER_TYPE order_type,
                              const CSymbol *symbol_obj,
                              const string source_method,
                              const bool mess=true)
  {
   ::ResetLastError();
   //--- Get the type of a market order by a trading operation type
   ENUM_ORDER_TYPE action=this.DirectionByActionType((ENUM_ACTION_TYPE)order_type);
   //--- Get the value of free funds to be left after conducting a trading operation
   double money_free=
     (
      //--- For MQL5, calculate the difference between free funds and the funds required to conduct a trading operation
      #ifdef __MQL5__  this.m_account.MarginFree()-this.m_account.MarginForAction(action,symbol_obj.Name(),volume,price)
      //--- For MQL4, use the operation result of the standard function returning the amount of funds left
      #else/*__MQL4__*/::AccountFreeMarginCheck(symbol_obj.Name(),action,volume) #endif 
     );
   //--- If no free funds are left, inform of that and return 'false'
   if(money_free<=0 #ifdef __MQL4__ || ::GetLastError()==134 #endif )
     {
      this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR);
      return false;
     }
   //--- Verification successful
   return true;
  }
//+------------------------------------------------------------------+


Deklarieren Sie die Methoden zur Korrektur der Preise der Stop- und Pending-Orders, die Methode zur Korrektur des Volumens in der Handelsorder , die Methode, die angibt, wie mit dem Fehler umzugehen ist, und die Methode zur Korrektur von Fehlern im Handelsauftrag:

//--- Return the correct (1) StopLoss, (2) TakeProfit and (3) order placement price
   double               CorrectStopLoss(const ENUM_ORDER_TYPE order_type,
                                       const double price_set,
                                       const double stop_loss,
                                       const CSymbol *symbol_obj,
                                       const uint spread_multiplier=1);
   double               CorrectTakeProfit(const ENUM_ORDER_TYPE order_type,
                                       const double price_set,
                                       const double take_profit,
                                       const CSymbol *symbol_obj,
                                       const uint spread_multiplier=1);
   double               CorrectPricePending(const ENUM_ORDER_TYPE order_type,
                                       const double price_set,
                                       const double price,
                                       const CSymbol *symbol_obj,
                                       const uint spread_multiplier=1);
//--- Return the volume, at which it is possible to open a position
   double               CorrectVolume(const double price,
                                       const ENUM_ORDER_TYPE order_type,
                                       const CSymbol *symbol_obj,
                                       const string source_method);

//--- Return the error handling method
   ENUM_ERROR_CODE_PROCESSING_METHOD   ResultProccessingMethod(void);
//--- Correct errors
   ENUM_ERROR_CODE_PROCESSING_METHOD   RequestErrorsCorrecting(MqlTradeRequest &request,const ENUM_ORDER_TYPE order_type,const uint spread_multiplier,CSymbol *symbol_obj);

public:


Ergänzen Sie die Angabe der Methode zur Überprüfung der Einschränkungen und Fehler und ersetzen Sie den zurückgegebenen Typ von 'bool' durch ENUM_ERROR_CODE_PROCESSING_METHOD:

//--- Check limitations and errors
   ENUM_ERROR_CODE_PROCESSING_METHOD CheckErrors(const double volume,
                                                 const double price,
                                                 const ENUM_ACTION_TYPE action,
                                                 const ENUM_ORDER_TYPE order_type,
                                                 CSymbol *symbol_obj,
                                                 const CTradeObj *trade_obj,
                                                 const string source_method,
                                                 const double limit=0,
                                                 double sl=0,
                                                 double tp=0);


Die Methode ist jetzt vollständiger geworden — sie prüft sofort auf mögliche Methoden zur Korrektur von Fehlern in einem Handelsauftrag, und jetzt gibt die Methode die Art und Weise zurück, wie man mit dem festgestellten Fehler umgehen kann. Zuvor gab sie lediglich das Flag für die erfolgreiche Prüfung zurück.

Deklarieren Sie die Methode zur Einstellung des Spread-Multiplikators:

//--- Set the following for symbol trading objects:
//--- (1) correct filling policy, (2) filling policy,
//--- (3) correct order expiration type, (4) order expiration type,
//--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date,
//--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) spread multiplier
   void                 SetCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL);
   void                 SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL);
   void                 SetCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL);
   void                 SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL);
   void                 SetMagic(const ulong magic,const string symbol=NULL);
   void                 SetComment(const string comment,const string symbol=NULL);
   void                 SetDeviation(const ulong deviation,const string symbol=NULL);
   void                 SetVolume(const double volume=0,const string symbol=NULL);
   void                 SetExpiration(const datetime expiration=0,const string symbol=NULL);
   void                 SetAsyncMode(const bool mode=false,const string symbol=NULL);
   void                 SetLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol=NULL);
   void                 SetSpreadMultiplier(const uint value=1,const string symbol=NULL);


Fügen Sie die Methoden zum Setzen und die Rückgabe des Flags hinzu, das den Handel für einen EA ermöglicht:

//--- Set/return the flag enabling sounds
   void                 SetUseSounds(const bool flag);
   bool                 IsUseSounds(void)                            const { return this.m_use_sound;       }

//--- Set/return the flag enabling trading
   void                 SetTradingDisableFlag(const bool flag)             { this.m_is_trade_disable=flag;  }
   bool                 IsTradingDisable(void)                       const { return this.m_is_trade_disable;}

//--- Open (1) Buy, (2) Sell position


Es kann Fehler geben, die den weiteren Handel nach ihrer Entdeckung verhindern, z.B. das vollständige Verbot des Handels für ein Konto. Dieses Flag wird gesetzt, wenn solche Fehler entdeckt werden und verhindert, dass weitere nutzlose Handelsaufträge gesendet werden.

Im Klassenkonstruktor setzen Sie das Flag zurück, das den Handel deaktiviert, und setzen Sie das Standard-EA-Verhalten im Falle von Handelsanforderungen als "korrekte Parameter":

//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CTrading::CTrading()
  {
   this.m_list_errors.Clear();
   this.m_list_errors.Sort();
   this.m_log_level=LOG_LEVEL_ALL_MSG;
   this.m_is_trade_disable=false;
   this.m_err_handling_behavior=ERROR_HANDLING_BEHAVIOR_CORRECT;
   ::ZeroMemory(this.m_request);
  }
//+------------------------------------------------------------------+


Das EA-Verhalten bei Fehlern in den Handelsmethoden kann dann über die EA-Einstellungen eingestellt werden. Wir werden jedoch die Methode der automatischen Korrektur verwenden, bis alle Behandlungsformen fertiggestellt sind.

Die aktuelle Implementierung der Methoden, die die Rückgabecodes des Handelsservers behandeln, liefert nur das Erfolgsflag zurück:

//+------------------------------------------------------------------+
//| Return the error handling method                                 |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::ResultProccessingMethod(void)
  {
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+


Warum wohl? Wir besprechen diese Methode im aktuellen Artikel nicht, da wir im nächsten Artikel die Behandlung der Rückgabecodes des Handelsservers implementieren werden. Die Methode wurde jedoch bereits beschrieben und implementiert, wenn auch in ihrer vereinfachten Form.

Implementierung der Methode zur Korrektur von Fehlern in einer Handelsordnung:

//+------------------------------------------------------------------+
//| Correct errors                                                   |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::RequestErrorsCorrecting(MqlTradeRequest &request,
                                                                    const ENUM_ORDER_TYPE order_type,
                                                                    const uint spread_multiplier,
                                                                    CSymbol *symbol_obj)
  {
//--- The empty error list means no errors are detected, return success
   int total=this.m_list_errors.Total();
   if(total==0)
      return ERROR_CODE_PROCESSING_METHOD_OK;
//--- In the current implementation, all these codes are temporarily handled by interrupting a trading request
   if(
      this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED)       || // Trading is disabled for the current account
      this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED)    || // Trading on the trading server side is disabled for EAs on the current account
      this.IsPresentErorCode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED)      || // Trading operations are disabled in the terminal
      this.IsPresentErorCode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)            || // Trading operations are disabled for the EA
      this.IsPresentErorCode(MSG_SYM_TRADE_MODE_DISABLED)                  || // Trading on a symbol is disabled 
      this.IsPresentErorCode(MSG_SYM_TRADE_MODE_CLOSEONLY)                 || // Close only
      this.IsPresentErorCode(MSG_SYM_MARKET_ORDER_DISABLED)                || // Market orders disabled
      this.IsPresentErorCode(MSG_SYM_LIMIT_ORDER_DISABLED)                 || // Limit orders are disabled
      this.IsPresentErorCode(MSG_SYM_STOP_ORDER_DISABLED)                  || // Stop orders are disabled
      this.IsPresentErorCode(MSG_SYM_STOP_LIMIT_ORDER_DISABLED)            || // StopLimit orders are disabled
      this.IsPresentErorCode(MSG_SYM_TRADE_MODE_SHORTONLY)                 || // Only short positions are allowed
      this.IsPresentErorCode(MSG_SYM_TRADE_MODE_LONGONLY)                  || // Only long positions are allowed
      this.IsPresentErorCode(MSG_SYM_CLOSE_BY_ORDER_DISABLED)              || // CloseBy orders are disabled
      this.IsPresentErorCode(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED)       || // Exceeded maximum allowed aggregate volume of orders and positions in one direction
      this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED)        || // Close by is disabled
      this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL)        || // Symbols of opposite positions are not equal
      this.IsPresentErorCode(MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ)   || // Unsupported price parameter type in a request
      this.IsPresentErorCode(MSG_LIB_TEXT_TRADING_DISABLE)                 || // Trading disabled for the EA until the reason is eliminated
      this.IsPresentErorCode(10006)                                        || // Request rejected
      this.IsPresentErorCode(10011)                                        || // Request handling error
      this.IsPresentErorCode(10012)                                        || // Request rejected due to expiration
      this.IsPresentErorCode(10013)                                        || // Invalid request
      this.IsPresentErorCode(10017)                                        || // Trading disabled
      this.IsPresentErorCode(10018)                                        || // Market closed
      this.IsPresentErorCode(10023)                                        || // Order status changed
      this.IsPresentErorCode(10025)                                        || // No changes in the request
      this.IsPresentErorCode(10026)                                        || // Auto trading disabled by server
      this.IsPresentErorCode(10027)                                        || // Auto trading disabled by client terminal
      this.IsPresentErorCode(10032)                                        || // Transaction is allowed for live accounts only
      this.IsPresentErorCode(10033)                                        || // The maximum number of pending orders is reached
      this.IsPresentErorCode(10034)                                           // You have reached the maximum order and position volume for this symbol
     ) return ERROR_CODE_PROCESSING_METHOD_EXIT;
//--- View the full list of errors and correct trading request parameters
   for(int i=0;i<total;i++)
     {
      int err=this.m_list_errors.At(i);
      if(err==NULL)
         continue;
      switch(err)
        {
         //--- Correct an invalid volume and stop levels in a trading request
         case MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME :
         case MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME :
         case MSG_LIB_TEXT_INVALID_VOLUME_STEP     :  request.volume=symbol_obj.NormalizedLot(request.volume);                                              break;
         case MSG_SYM_SL_ORDER_DISABLED            :  request.sl=0;                                                                                         break;
         case MSG_SYM_TP_ORDER_DISABLED            :  request.tp=0;                                                                                         break;
         case MSG_LIB_TEXT_PR_LESS_STOP_LEVEL      :  request.price=this.CorrectPricePending(order_type,request.price,0,symbol_obj,spread_multiplier);      break;
         case MSG_LIB_TEXT_SL_LESS_STOP_LEVEL      :  request.sl=this.CorrectStopLoss(order_type,request.price,request.sl,symbol_obj,spread_multiplier);    break;
         case MSG_LIB_TEXT_TP_LESS_STOP_LEVEL      :  request.tp=this.CorrectTakeProfit(order_type,request.price,request.tp,symbol_obj,spread_multiplier);  break;
         //--- If unable to select the position lot, return "abort trading attempt" since the funds are insufficient even for the minimum lot
         case MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR    :  request.volume=this.CorrectVolume(request.volume,request.price,order_type,symbol_obj,DFUN);
                                                      if(request.volume==0)
                                                         return ERROR_CODE_PROCESSING_METHOD_EXIT;                                                                                      break;
         //--- Proximity to the order activation level is handled by five-second waiting - during this time, the price may go beyond the freeze level
         case MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL    :
         case MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL    :
         case MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL    :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT - wait 5 seconds
         default:
           break;
        }
     }
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+


Die Logik der Methode wird in den Code-Kommentaren beschrieben. Kurz gesagt: Wenn Fehlercodes entdeckt werden, die noch nicht behandelt werden können, geben wir die Behandlungsmethode "Abbruch des Handelsversuchs" zurück. Im Falle von Fehlern, die korrigiert werden können, korrigieren Sie die Parameterwerte und geben ОК zurück.

Verbesserung der Methode zur Überprüfung von Handelseinschränkungen und Fehlern von Handelsaufträgen:

//+------------------------------------------------------------------+
//| Check limitations and errors                                     |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::CheckErrors(const double volume,
                                                        const double price,
                                                        const ENUM_ACTION_TYPE action,
                                                        const ENUM_ORDER_TYPE order_type,
                                                        CSymbol *symbol_obj,
                                                        const CTradeObj *trade_obj,
                                                        const string source_method,
                                                        const double limit=0,
                                                        double sl=0,
                                                        double tp=0)
  {
//--- Check the previously set flag disabling trading for an EA
   if(this.IsTradingDisable())
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_FATAL_ERROR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return ERROR_CODE_PROCESSING_METHOD_DISABLE;
     }
//--- result of all checks and error flags
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   bool res=true;
//--- Clear the error list
   this.m_list_errors.Clear();
   this.m_list_errors.Sort();
//--- Check trading limitations
   res &=this.CheckTradeConstraints(volume,action,symbol_obj,source_method,sl,tp);
//--- Check the funds sufficiency for opening positions/placing orders
   if(action<ACTION_TYPE_CLOSE_BY)
      res &=this.CheckMoneyFree(volume,price,order_type,symbol_obj,source_method);
//--- Check parameter values by StopLevel and FreezeLevel
   res &=this.CheckLevels(action,order_type,price,limit,sl,tp,symbol_obj,source_method);
   
//--- If there are limitations, display the header and the error list
   if(!res)
     {
      //--- Request was rejected before sending to the server due to:
      int total=this.m_list_errors.Total();
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         //--- For MQL5, first display the list header followed by the error list
         #ifdef __MQL5__
         ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST));
         for(int i=0;i<total;i++)
            ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
         //--- For MQL4, the journal messages are displayed in the reverse order: the error list in the reverse loop is followed by the list header
         #else    
         for(int i=total-1;i>WRONG_VALUE;i--)
            ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
         ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST));
         #endif 
        }
      //--- If the action is performed at the "abort trading operation" error
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
         return ERROR_CODE_PROCESSING_METHOD_EXIT;
      //--- If the action is performed at the "create a pending request" error
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST)
         return ERROR_CODE_PROCESSING_METHOD_PENDING;
      //--- If the action is performed at the "correct parameters" error
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_CORRECT)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST));
         //--- Return the result of an attempt to correct the request parameters
         return this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj);
        }
     }
//--- No limitations
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+


Der ergänzte Code ist gelb markiert. Nun wird in der Methode zunächst das Flag geprüft, das den Handel deaktiviert. Wenn es gesetzt ist, wird die Fehlerbehandlungsart "Handel für EA deaktivieren" zurückgegeben. Als Nächstes wird je nach dem spezifizierten EA-Verhalten bei Fehlern und entsprechend dem Fehlercode die erforderliche Fehlerbehandlungsmethode zurückgegeben. Wenn es keine Fehler gibt, wird der Code, der keine Fehlerbehandlung erfordert, zurückgegeben.

Die Methode zur Überprüfung von Handelsbeschränkungen wurde mehrfach ähnlich geändert, indem die notwendigen Flags hinzugefügt wurden, die das Vorhandensein verschiedener Fehlertypen und deren Behandlung anzeigen.
Alle in der Methode durchgeführten Aktionen sowie deren Logik werden in den Code-Kommentaren sehr detailliert beschrieben. Schauen wir uns daher die fertiggestellte Methode an:

//+------------------------------------------------------------------+
//| Check trading limitations                                        |
//+------------------------------------------------------------------+
bool CTrading::CheckTradeConstraints(const double volume,
                                     const ENUM_ACTION_TYPE action_type,
                                     const CSymbol *symbol_obj,
                                     const string source_method,
                                     double sl=0,
                                     double tp=0)
  {
//--- the result of conducting all checks
   bool res=true;
//--- Check connection with the trade server (not in the test mode)
   if(!::TerminalInfoInteger(TERMINAL_CONNECTED))
     {
      if(!::MQLInfoInteger(MQL_TESTER))
        {
         //--- Write the error code to the list and return 'false' - there is no point in further checks
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(10031);
         return false;
        }
     }
//--- Check if trading is enabled for an account (if there is a connection with the trade server)
   else if(!this.m_account.TradeAllowed())
     {
      //--- Write the error code to the list and return 'false' - there is no point in further checks
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED);
      return false;
     }
//--- Check if trading is allowed for any EAs/scripts for the current account
   if(!this.m_account.TradeExpert())
     {
      //--- Write the error code to the list and return 'false' - there is no point in further checks
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED);
      return false;
     }
//--- Check if auto trading is allowed in the terminal.
//--- AutoTrading button (Options --> Expert Advisors --> "Allowed automated trading")
   if(!::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      //--- Write the error code to the list and return 'false' - there is no point in further checks
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED);
      return false;
     }
//--- Check if auto trading is allowed for the current EA.
//--- (F7 --> Common --> Allow Automated Trading)
   if(!::MQLInfoInteger(MQL_TRADE_ALLOWED))
     {
      //--- Write the error code to the list and return 'false' - there is no point in further checks
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED);
      return false;
     }
//--- Check if trading is enabled on a symbol.
//--- If trading is disabled, write the error code to the list and return 'false' - there is no point in further checks
   if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_DISABLED)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
      this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_DISABLED);
      return false;
     }

//--- If not closing/removal/modification
   if(action_type<ACTION_TYPE_CLOSE_BY)
     {
      //--- In case of close-only, write the error code to the list and return 'false' - there is no point in further checks
      if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)
        {
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY);
         return false;
        }
      //--- Check the minimum volume
      if(volume<symbol_obj.LotsMin())
        {
         //--- The volume in a request is less than the minimum allowed one.
         //--- add the error code to the list
         this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME);
         //--- If the EA behavior during the trading error is set to "abort trading operation",
         //--- return 'false' - there is no point in further checks
         if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
            return false;
         //--- If the EA behavior during a trading error is set to
         //--- "correct parameters" or "create a pending request",
         //--- write 'false' to the result
         else res &=false;
        }
      //--- Check the maximum volume
      else if(volume>symbol_obj.LotsMax())
        {
         //--- The volume in the request exceeds the maximum acceptable one.
         //--- add the error code to the list
         this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME);
         //--- If the EA behavior during the trading error is set to "abort trading operation",
         //--- return 'false' - there is no point in further checks
         if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
            return false;
         //--- If the EA behavior during a trading error is set to
         //--- "correct parameters" or "create a pending request",
         //--- write 'false' to the result
         else res &=false;
        }
      //--- Check the minimum volume gradation
      double step=symbol_obj.LotsStep();
      if(fabs((int)round(volume/step)*step-volume)>0.0000001)
        {
         //--- The volume in the request is not a multiple of the minimum gradation of the lot change step
         //--- add the error code to the list
         this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP);
         //--- If the EA behavior during the trading error is set to "abort trading operation",
         //--- return 'false' - there is no point in further checks
         if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
            return false;
         //--- If the EA behavior during a trading error is set to
         //--- "correct parameters" or "create a pending request",
         //--- write 'false' to the result
         else res &=false;
        }
     }

//--- When opening a position
   if(action_type<ACTION_TYPE_BUY_LIMIT)
     {
      //--- Check if sending market orders is allowed on a symbol.
      //--- If using market orders is disabled, write the error code to the list and return 'false' - there is no point in further checks
      if(!symbol_obj.IsMarketOrdersAllowed())
        {
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_MARKET_ORDER_DISABLED);
         return false;
        }
     }
//--- When placing a pending order
   else if(action_type>ACTION_TYPE_SELL && action_type<ACTION_TYPE_CLOSE_BY)
     {
      //--- If there is a limitation on the number of pending orders on an account and placing a new order exceeds it
      if(this.m_account.LimitOrders()>0 && this.OrdersTotalAll()+1 > this.m_account.LimitOrders())
        {
         //--- The limit on the number of pending orders is reached - write the error code to the list and return 'false' - there is no point in further checks
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(10033);
         return false;
        }  
      //--- Check if placing limit orders is allowed on a symbol.
      if(action_type==ACTION_TYPE_BUY_LIMIT || action_type==ACTION_TYPE_SELL_LIMIT)
        {
         //--- If setting limit orders is disabled, write the error code to the list and return 'false' - there is no point in further checks
         if(!symbol_obj.IsLimitOrdersAllowed())
           {
            this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_SYM_LIMIT_ORDER_DISABLED);
            return false;
           }
        }
      //--- Check if placing stop orders is allowed on a symbol.
      else if(action_type==ACTION_TYPE_BUY_STOP || action_type==ACTION_TYPE_SELL_STOP)
        {
         //--- If setting stop orders is disabled, write the error code to the list and return 'false' - there is no point in further checks
         if(!symbol_obj.IsStopOrdersAllowed())
           {
            this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_SYM_STOP_ORDER_DISABLED);
            return false;
           }
        }
      //--- For MQL5, check if placing stop limit orders is allowed on a symbol.
      #ifdef __MQL5__
      else if(action_type==ACTION_TYPE_BUY_STOP_LIMIT || action_type==ACTION_TYPE_SELL_STOP_LIMIT)
        {
         //--- If setting stop limit orders is disabled, write the error code to the list and return 'false' - there is no point in further checks
         if(!symbol_obj.IsStopLimitOrdersAllowed())
           {
            this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_SYM_STOP_LIMIT_ORDER_DISABLED);
            return false;
           }
        }
      #endif 
     }

//--- In case of opening/placing/modification
   if(action_type!=ACTION_TYPE_CLOSE_BY)
     {
      //--- If not modification
      if(action_type!=ACTION_TYPE_MODIFY)
        {
         //--- When buying, check if long trading is enabled on a symbol
         if(this.DirectionByActionType(action_type)==ORDER_TYPE_BUY)
           {
            //--- If only short positions are enabled, write the error code to the list and return 'false' - there is no point in further checks
            if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY)
              {
               this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
               this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_SHORTONLY);
               return false;
              }
            //--- If a symbol has the limitation on the total volume of an open position and pending orders in the same direction   
            if(symbol_obj.VolumeLimit()>0)
              {
               //--- (If the total volume of placed long orders and open long positions)+open volume exceed the maximum one
               if(this.OrdersTotalVolumeLong()+this.PositionsTotalVolumeLong()+volume > symbol_obj.VolumeLimit())
                 {
                  //--- Exceeded maximum allowed aggregate volume of orders and positions in one direction
                  //--- write the error code to the list and return 'false' - there is no point in further checks
                  this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
                  this.AddErrorCodeToList(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED);
                  return false;
                 }
              }
           }
         //--- When selling, check if short trading is enabled on a symbol
         else if(this.DirectionByActionType(action_type)==ORDER_TYPE_SELL)
           {
            //--- If only long positions are enabled, write the error code to the list and return 'false' - there is no point in further checks
            if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY)
              {
               this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
               this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_LONGONLY);
               return false;
              }
            //--- If a symbol has the limitation on the total volume of an open position and pending orders in the same direction   
            if(symbol_obj.VolumeLimit()>0)
              {
               //--- (If the total volume of placed short orders and open short positions)+open volume exceed the maximum one
               if(this.OrdersTotalVolumeShort()+this.PositionsTotalVolumeShort()+volume > symbol_obj.VolumeLimit())
                 {
                  //--- Exceeded maximum allowed aggregate volume of orders and positions in one direction
                  //--- write the error code to the list and return 'false' - there is no point in further checks
                  this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
                  this.AddErrorCodeToList(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED);
                  return false;
                 }
              }
           }
        }
      //--- If the request features StopLoss and its placing is not allowed
      if(sl>0 && !symbol_obj.IsStopLossOrdersAllowed())
        {
         //--- add the error code to the list
         this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_SL_ORDER_DISABLED);
         //--- If the EA behavior during the trading error is set to "abort trading operation",
         //--- return 'false' - there is no point in further checks
         if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
            return false;
         //--- If the EA behavior during a trading error is set to
         //--- "correct parameters" or "create a pending request",
         //--- write 'false' to the result
         else res &=false;
        }
      //--- If the request features TakeProfit and its placing is not allowed
      if(tp>0 && !symbol_obj.IsTakeProfitOrdersAllowed())
        {
         //--- add the error code to the list
         this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_TP_ORDER_DISABLED);
         //--- If the EA behavior during the trading error is set to "abort trading operation",
         //--- return 'false' - there is no point in further checks
         if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
            return false;
         //--- If the EA behavior during a trading error is set to
         //--- "correct parameters" or "create a pending request",
         //--- write 'false' to the result
         else res &=false;
        }
     }

//--- Beim Schließen durch einen Gegenposition
   else if(action_type==ACTION_TYPE_CLOSE_BY)
     {
      //--- When closing by an opposite position is disabled
      if(!symbol_obj.IsCloseByOrdersAllowed())
        {
         //--- write the error code to the list and return 'false'
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED);
         return false;
        }
     }
   return res;
  }
//+------------------------------------------------------------------+


Bei der Methode, die die Parameterwerte nach StopLevel und FreezeLevel zurückgibt, fügen Sie zu jedem erkannten Fehler das Flag hinzu, das angibt, dass ein Fehler in der Fehlerliste angezeigt werden soll:

//+------------------------------------------------------------------+
//| Check parameter values by StopLevel and FreezeLevel              |
//+------------------------------------------------------------------+
bool CTrading::CheckLevels(const ENUM_ACTION_TYPE action,
                           const ENUM_ORDER_TYPE order_type,
                           double price,
                           double limit,
                           double sl,
                           double tp,
                           const CSymbol *symbol_obj,
                           const string source_method)
  {
//--- the result of conducting all checks
   bool res=true;
//--- StopLevel
//--- If this is not a position closure/order removal
   if(action!=ACTION_TYPE_CLOSE && action!=ACTION_TYPE_CLOSE_BY)
     {
      //--- When placing a pending order
      if(action>ACTION_TYPE_SELL)
        {
         //--- If the placement distance in points is less than StopLevel
         if(!this.CheckPriceByStopLevel(order_type,price,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_STOP_LEVEL);
            res &=false;
           }
        }
      //--- If StopLoss is present
      if(sl>0)
        {
         //--- If StopLoss distance in points from the open price is less than StopLevel
         double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price);
         if(!this.CheckStopLossByStopLevel(order_type,price_open,sl,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_STOP_LEVEL);
            res &=false;
           }
        }
      //--- If TakeProfit is present
      if(tp>0)
        {
         double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price);
         //--- If TakeProfit distance in points from the open price is less than StopLevel
         if(!this.CheckTakeProfitByStopLevel(order_type,price_open,tp,symbol_obj))
           {
            //--- add the error code to the list and write 'false' to the result
            this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_STOP_LEVEL);
            res &=false;
           }
        }
     }
//--- FreezeLevel
//--- If this is a position closure/order removal/modification
   if(action>ACTION_TYPE_SELL_STOP_LIMIT)
     {
      //--- If this is a position
      if(order_type<ORDER_TYPE_BUY_LIMIT)
        {
         //--- StopLoss modification
         if(sl>0)
           {
            //--- If the distance from the price to StopLoss is less than FreezeLevel
            if(!this.CheckStopLossByFreezeLevel(order_type,sl,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
               this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
         //--- TakeProfit modification
         if(tp>0)
           {
            //--- If the distance from the price to StopLoss is less than FreezeLevel
            if(!this.CheckTakeProfitByFreezeLevel(order_type,tp,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
               this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
        }
      //--- If this is a pending order
      else
        {
         //--- Placement price modification
         if(price>0)
           {
            //--- If the distance from the price to the order activation price is less than FreezeLevel
            if(!this.CheckPriceByFreezeLevel(order_type,price,symbol_obj))
              {
               //--- add the error code to the list and write 'false' to the result
               this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
               this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL);
               res &=false;
              }
           }
        }
     }
   return res;
  }
//+------------------------------------------------------------------+


Bei der Methode zur Festlegung der Preise für Handelsanfragen fügen Sie die Aktualisierung der Preise hinzu und, im Falle eines Aktualisierungsfehlers, geben Sie den entsprechenden Fehlercode zurück:

//+------------------------------------------------------------------+
//| Set trading request prices                                       |
//+------------------------------------------------------------------+
template <typename PR,typename SL,typename TP,typename PL> 
bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PR price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj)
  {
//--- Reset prices
   ::ZeroMemory(this.m_request);
//--- Update all data by symbol
   if(!symbol_obj.RefreshRates())
     {
      this.AddErrorCodeToList(10021);
      return false;
     }

//--- Open/close price


Auch die Preiskalkulation in der Methode zur Festlegung der Preise für Handelsaufträge wurde geändert:

         //--- Calculate the order price
         switch((int)action)
           {
            //--- Pending order
            case ORDER_TYPE_BUY_LIMIT       :  this.m_request.price=::NormalizeDouble(symbol_obj.Ask()-price*symbol_obj.Point(),symbol_obj.Digits());      break;
            case ORDER_TYPE_BUY_STOP        :
            case ORDER_TYPE_BUY_STOP_LIMIT  :  this.m_request.price=::NormalizeDouble(symbol_obj.Ask()+price*symbol_obj.Point(),symbol_obj.Digits());      break;
            
            case ORDER_TYPE_SELL_LIMIT      :  this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits());  break;
            case ORDER_TYPE_SELL_STOP       :
            case ORDER_TYPE_SELL_STOP_LIMIT :  this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits());  break;
            //--- Default - current position open prices
            default  :  this.m_request.price=
              (
               this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.Ask(),symbol_obj.Digits()) : 
               ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits())
              ); break;
           }


Jetzt wurde die Klassenmethode Bid() durch die Methode BidLast() ersetzt, die je nach Diagrammkonstruktionsmodus entweder den Bid-Preis oder den letzten Preis zurückgibt.

Eine Methode, die den Spread-Multiplikator für Handelsobjekte aller Symbole festlegt:

//+------------------------------------------------------------------+
//| Set the spread multiplier                                        |
//| for trading objects of all symbols                               |
//+------------------------------------------------------------------+
void CTrading::SetSpreadMultiplier(const uint value=1,const string symbol=NULL)
  {
   CSymbol *symbol_obj=NULL;
   if(symbol==NULL)
     {
      CArrayObj *list=this.m_symbols.GetList();
      if(list==NULL || list.Total()==0)
         return;
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol_obj=list.At(i);
         if(symbol_obj==NULL)
            continue;
         CTradeObj *trade_obj=symbol_obj.GetTradeObj();
         if(trade_obj==NULL)
            continue;
         trade_obj.SetSpreadMultiplier(value);
        }
     }
   else
     {
      CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol,DFUN);
      if(trade_obj==NULL)
         return;
      trade_obj.SetSpreadMultiplier(value);
     }
  }
//+------------------------------------------------------------------+


Die Methode erhält den Multiplikator (Standard ist 1) und einen Symbolnamen (Standard ist NULL).

Wenn NULL für das Symbol übergeben wird, wird der Multiplikator für Handelsobjekte aller Symbole der bestehenden Symbolkollektion gesetzt.
Andernfalls wird der Wert einem Handelsobjekt eines Symbols zugewiesen, dessen Name an die Methode übergeben wurde.

Aufgrund der neuen Fehlerbehandlung wurden alle Handelsmethoden verfeinert.
Betrachten wir den Code der Methode zur Eröffnung einer Kaufposition:

//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenBuy(const double volume,
                       const string symbol,
                       const ulong magic=ULONG_MAX,
                       const SL sl=0,
                       const TP tp=0,
                       const string comment=NULL,
                       const ulong deviation=ULONG_MAX)
  {
//--- Set the trading request result as 'true' and the error flag as "no errors"
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_BUY;
   ENUM_ORDER_TYPE order_type=ORDER_TYPE_BUY;
//--- Get a symbol object by a symbol name. If failed to get
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
//--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false'
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
//--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false'
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Set the prices
//--- If failed to set - write the "internal error" flag, display the message in the journal and return 'false'
   if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));
      return false;
     }

//--- Write the volume to the request structure
   this.m_request.volume=volume;
//--- Get the method of handling errors from the CheckErrors() method while checking for errors
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume,symbol_obj.Ask(),action,order_type,symbol_obj,trade_obj,DFUN,0,this.m_request.sl,this.m_request.tp);
//--- In case of trading limitations, funds insufficiency,
//--- if there are limitations by StopLevel or FreezeLevel ...
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- If trading is disabled completely, display a journal message, play the error sound and exit
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- If the check result is "abort trading operation" - display a journal message, play the error sound and exit
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- If the check result is "waiting", display the message in the journal
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST));
         //--- Instead of creating a pending request, we temporarily wait the required time period (the CheckErrors() method result is returned)
         ::Sleep(method);
         //--- after waiting, update all data
         symbol_obj.Refresh();
        }
      //--- If the check result is "create a pending request", do nothing temporarily
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST));
        }
     }
   
//--- Send the request
   res=trade_obj.OpenPosition(POSITION_TYPE_BUY,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,comment,deviation);
//--- If the request is successful, play the success sound set for a symbol trading object for this type of trading operation
   if(res)
     {
      if(this.IsUseSounds())
         trade_obj.PlaySoundSuccess(action,order_type);
     }
//--- If the request is not successful, play the error sound set for a symbol trading object for this type of trading operation
   else
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.IsUseSounds())
         trade_obj.PlaySoundError(action,order_type);
     }
//--- Return the result of sending a trading request in a symbol trading object
   return res;
  }
//+------------------------------------------------------------------+


Alle Klarstellungen werden in den Code-Kommentaren im Detail vorgenommen. Andere Handelsmethoden wurden in ähnlicher Weise verbessert. Ich hoffe, hier ist alles klar. In jedem Fall können Sie gerne den Abschnitt Kommentare verwenden.

Die Methoden, die die berechneten, gültigen Preise der Stop- und Pending-Orders zurückgeben:

//+------------------------------------------------------------------+
//| Rückgabe des korrekten StopLevels relativ zum StopLevel           |
//+------------------------------------------------------------------+
double CTrading::CorrectStopLoss(const ENUM_ORDER_TYPE order_type,const double price_set,const double stop_loss,const CSymbol *symbol_obj,const uint spread_multiplier=1)
  {
   if(stop_loss==0) return 0;
   uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel());
   double price=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ? symbol_obj.Ask() : price_set);
   return
     (this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY            ?
      ::NormalizeDouble(fmin(price-lv*symbol_obj.Point(),stop_loss),symbol_obj.Digits())  :
      ::NormalizeDouble(fmax(price+lv*symbol_obj.Point(),stop_loss),symbol_obj.Digits())
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des korrekten TakeProfit relativ zum StopLevel          |
//+------------------------------------------------------------------+
double CTrading::CorrectTakeProfit(const ENUM_ORDER_TYPE order_type,const double price_set,const double take_profit,const CSymbol *symbol_obj,const uint spread_multiplier=1)
  {
   if(take_profit==0) return 0;
   uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel());
   double price=(order_type==ORDER_TYPE_BUY ? symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ? symbol_obj.Ask() : price_set);
   return
     (this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY             ?
      ::NormalizeDouble(fmax(price+lv*symbol_obj.Point(),take_profit),symbol_obj.Digits()) :
      ::NormalizeDouble(fmin(price-lv*symbol_obj.Point(),take_profit),symbol_obj.Digits())
     );
  }
//+------------------------------------------------------------------+
//| Rückgabe des Preises der korrekten Order-Platzierung             |
//| relative to StopLevel                                            |
//+------------------------------------------------------------------+
double CTrading::CorrectPricePending(const ENUM_ORDER_TYPE order_type,const double price_set,const double price,const CSymbol *symbol_obj,const uint spread_multiplier=1)
  {
   uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel());
   double pp=0;
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ? symbol_obj.Ask()     : price); return ::NormalizeDouble(fmin(pp-lv*symbol_obj.Point(),price_set),symbol_obj.Digits());
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ? symbol_obj.Ask()     : price); return ::NormalizeDouble(fmax(pp+lv*symbol_obj.Point(),price_set),symbol_obj.Digits());
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ? symbol_obj.BidLast() : price); return ::NormalizeDouble(fmax(pp+lv*symbol_obj.Point(),price_set),symbol_obj.Digits());
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ? symbol_obj.BidLast() : price); return ::NormalizeDouble(fmin(pp-lv*symbol_obj.Point(),price_set),symbol_obj.Digits());
      default                          :  if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),::EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+


Hier sollte auch ohne Code-Kommentare alles klar sein — die an die Methoden übergebenen Preise werden mit dem Preis verglichen, der als StopLevel-Abstand vom Eröffnungspreis erzielt wird. Der gültige (je nach Auftragsart höhere/niedrigere) Preis wird an das aufrufende Programm zurückgegeben.

Die Methode, die das Volumen zurückgibt, zu dem eine Position eröffnet werden kann:

//+------------------------------------------------------------------+
//| Return the volume, at which it is possible to open a position    |
//+------------------------------------------------------------------+
double CTrading::CorrectVolume(const double price,const ENUM_ORDER_TYPE order_type,CSymbol *symbol_obj,const string source_method)
  {
//--- If funds are insufficient for the minimum lot, inform of that and return zero
   if(!this.CheckMoneyFree(symbol_obj.LotsMin(),price,order_type,symbol_obj,source_method))
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(CMessage::Text(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT));
      return 0;
     }

//--- Update account and symbol data
   this.m_account.Refresh();
   symbol_obj.RefreshRates();
//--- Calculate the lot, which is closest to the acceptable one
   double vol=symbol_obj.NormalizedLot(this.m_account.Equity()*this.m_account.Leverage()/symbol_obj.TradeContractSize()/(symbol_obj.CurrencyBase()=="USD" ? 1.0 : symbol_obj.BidLast()));
//--- Calculate a sufficient lot
   double margin=this.m_account.MarginForAction(order_type,symbol_obj.Name(),1.0,price);
   if(margin!=EMPTY_VALUE)
      vol=symbol_obj.NormalizedLot(this.m_account.MarginFree()/margin);

//--- If the calculated lot is invalid or the margin calculation returns an error
   if(!this.CheckMoneyFree(vol,price,order_type,symbol_obj,source_method))
     {
      //--- In the do-while loop, while the calculated valid volume exceeds the minimum lot
      do
        {
         //--- Subtract the minimum lot from the valid lot value
         vol-=symbol_obj.LotsStep();
         //--- If the calculated lot allows opening a position/setting an order, return the lot value
         if(this.CheckMoneyFree(symbol_obj.NormalizedLot(vol),price,order_type,symbol_obj,source_method))
            return vol;
        }
      while(vol>symbol_obj.LotsMin() && !::IsStopped());
     }
//--- If the lot is calculated correctly, return the calculated lot
   else
      return vol;
//--- If the current stage is reached, the funds are insufficient. Inform of that and return zero
   if(this.m_log_level>LOG_LEVEL_NO_MSG)
      ::Print(CMessage::Text(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT));
   return 0;
  }
//+------------------------------------------------------------------+


Der Code wird hier auch kommentiert.
Zuerst prüfen wir die Fähigkeit, mit einem Mindestlot zu öffnen. Ist dies nicht möglich, sind weitere Berechnungen sinnlos — geben Sie Null zurück.
Als Nächstes berechnen wir die ungefähr zulässige Losgröße (um im Falle einer möglichen Anpassung nicht die gewünschte Losgröße von ihrem Maximalwert "zuwählen").
Als Nächstes berechnen Sie die maximale Losgröße, das die Eröffnung einer Position unter Verwendung aller verfügbaren Funktionen erlaubt. Warum das? Wenn die Mittel für die Eröffnung einer Position nicht ausreichen, bedeutet dies, dass das erforderliche Volumen groß war, was bedeutet, dass wir das maximal mögliche Volumen berechnen müssen.

In dieser Berechnung verwenden wir die Funktion OrderCalcMargin(), die im Falle eines Fehlers false zurückgeben kann, während die Methode MarginForAction() der Klasse CAccount, die diese Funktion verwendet, EMPTY_VALUE entsprechend dem konstanten Wert DBL_MAX (der maximale Wert, der durch den Typ doppelt dargestellt werden kann) zurückgibt. Wenn wir diesen Wert erhalten, liegt ein Fehler vor, und das Los wurde nicht berechnet.

In diesem Fall (nicht nur im Falle eines Fehlers, sondern auch bei der Überprüfung der Berechnungsgültigkeit) verwenden wir die "Auswahl" eines erforderlichen maximalen Loses, indem wir einfach den Losschritt vom berechneten maximal möglichen Volumen in der Handelsorder subtrahieren. Dazu benötigen wir das zuvor berechnete ungefähres verfügbares Volumen. Wenn wir das exakte Volumen nicht berechnen konnten, beginnt die Lot-Abnahme-Schleife mit dem nächstgelegenen Lot (und nicht mit dem für das Symbol festgelegten Maximal-Lot), wodurch die Anzahl der Schleifen-Iterationen stark reduziert wird.

Übrigens habe ich bei der Prüfung nicht die Funktionsfehler OrderCalcMargin() bei der Berechnung des Loses erhalten. Die ungültigen Berechnungen sind aber trotzdem aufgetreten (ungefähr, um eine Schrittweite der Losgrößenänderung).

Damit sind die Änderungen und Verbesserungen der Handelsklasse abgeschlossen.

Tests

Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn unter \MQL5\Experts\TestDoEasy\ Part24\ unter dem Namen TestDoEasyPart24.mq5.

Fügen wir das Flag für die Arbeit im Strategietester zur Liste der globalen Variablen hinzufügen:

//--- Globale Variablen
CEngine        engine;
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;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];
bool           testing;
//+------------------------------------------------------------------+


Setzen Sie in OnInit() den Wert des Flags für die Arbeit im Strategietester:

//+------------------------------------------------------------------+
//| 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();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   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);
     }


Um während der Arbeit im Tester Ereignisse an die Ereignisbehandlung OnDoEasyEvent() der Bibliothek zu senden, benötigen wir die spezielle Funktion EventsHandling().
Sie wurde geringfügig verbessert:

//+------------------------------------------------------------------+
//| Working with events in the tester                                |
//+------------------------------------------------------------------+
void EventsHandling(void)
  {
//--- If a trading event is present
   if(engine.IsTradeEvent())
     {
      //--- Number of trading events occurred simultaneously
      int total=engine.GetTradeEventsTotal();
      for(int i=0;i<total;i++)
        {
         //--- Get the next event from the list of simultaneously occurred events by index
         CEventBaseObj *event=engine.GetTradeEventByIndex(i);
         if(event==NULL)
            continue;
         long   lparam=i;
         double dparam=event.DParam();
         string sparam=event.SParam();
         OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
        }
     }
//--- If there is an account event
   if(engine.IsAccountsEvent())
     {
      //--- Get the list of all account events occurred simultaneously
      CArrayObj* list=engine.GetListAccountEvents();
      if(list!=NULL)
        {
         //--- Get the next event in a loop
         int total=list.Total();
         for(int i=0;i<total;i++)
           {
            //--- take an event from the list
            CEventBaseObj *event=list.At(i);
            if(event==NULL)
               continue;
            //--- Send an event to the event handler
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();
            OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
           }
        }
     }
//--- If there is a symbol collection event
   if(engine.IsSymbolsEvent())
     {
      //--- Get the list of all symbol events occurred simultaneously
      CArrayObj* list=engine.GetListSymbolsEvents();
      if(list!=NULL)
        {
         //--- Get the next event in a loop
         int total=list.Total();
         for(int i=0;i<total;i++)
           {
            //--- take an event from the list
            CEventBaseObj *event=list.At(i);
            if(event==NULL)
               continue;
            //--- Send an event to the event handler
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();
            OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
           }
        }
     }
  }
//+------------------------------------------------------------------+


Die Kommentare im Code sind hier ziemlich selbsterklärend.

Da wir nun die Liste der neuen Handelsereignisse erstellt haben, erhalten wir jedes Ereignis aus der Liste aller neuen Handelsereignisse im Ereignis-Handler der Bibliothek OnDoEasyEvent() durch einen Ereignisindex und zeigen einfach die Beschreibungen aller aus der Liste erhaltenen Ereignisse im Journal an:

//+------------------------------------------------------------------+
//| Handling DoEasy library events                                   |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
//--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
   
//--- Handling symbol events
   if(source==COLLECTION_SYMBOLS_ID)
     {
      CSymbol *symbol=engine.GetSymbolObjByName(sparam);
      if(symbol==NULL)
         return;
      //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol
      int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits());
      //--- Event text description
      string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx));
      //--- Property change text value
      string value=DoubleToString(dparam,digits);
      
      //--- Check event reasons and display its description in the journal
      if(reason==BASE_EVENT_REASON_INC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     }   
     
//--- Handling account events
   else if(source==COLLECTION_ACCOUNT_ID)
     {
      CAccount *account=engine.GetAccountCurrent();
      if(account==NULL)
         return;
      //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol
      int digits=int(idx<ACCOUNT_PROP_INTEGER_TOTAL ? 0 : account.CurrencyDigits());
      //--- Event text description
      string id_descr=(idx<ACCOUNT_PROP_INTEGER_TOTAL ? account.GetPropertyDescription((ENUM_ACCOUNT_PROP_INTEGER)idx) : account.GetPropertyDescription((ENUM_ACCOUNT_PROP_DOUBLE)idx));
      //--- Property change text value
      string value=DoubleToString(dparam,digits);
      
      //--- Checking event reasons and handling the increase of funds by a specified value,
      
      //--- In case of a property value increase
      if(reason==BASE_EVENT_REASON_INC)
        {
         //--- Display an event in the journal
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
         //--- if this is an equity increase
         if(idx==ACCOUNT_PROP_EQUITY)
           {
            //--- Abrufen der Liste aller offenen Positionen
            CArrayObj* list_positions=engine.GetListMarketPosition();
            //--- Select positions with the profit exceeding zero
            list_positions=CSelect::ByOrderProperty(list_positions,ORDER_PROP_PROFIT_FULL,0,MORE);
            if(list_positions!=NULL)
              {
               //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap
               list_positions.Sort(SORT_BY_ORDER_PROFIT_FULL);
               //--- Get the position index with the highest profit
               int index=CSelect::FindOrderMax(list_positions,ORDER_PROP_PROFIT_FULL);
               if(index>WRONG_VALUE)
                 {
                  COrder* position=list_positions.At(index);
                  if(position!=NULL)
                    {
                     //--- Get a ticket of a position with the highest profit and close the position by a ticket
                     engine.ClosePosition(position.Ticket());
                    }
                 }
              }
           }
        }
      //--- Other events are simply displayed in the journal
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     } 
     
//--- Handling market watch window events
   else if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      //--- Market Watch window event
      string descr=engine.GetMWEventDescription((ENUM_MW_EVENT)idx);
      string name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam);
      Print(TimeMSCtoString(lparam)," ",descr,name);
     }
//--- Handling trading events
   else if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      //--- Get the list of trading events
      CArrayObj *list=engine.GetListAllOrdersEvents();
      if(list==NULL)
         return;
      //--- get the event index shift relative to the end of the list
      //--- in the tester, the shift is passed by the lparam parameter to the event handler
      //--- outside the tester, events are sent one by one and handled in OnChartEvent()
      int shift=(testing ? (int)lparam : 0);
      CEvent *event=list.At(list.Total()-1-shift);
      if(event==NULL)
      return;
      //--- Accrue the credit
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CREDIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Additional charges
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CHARGE)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Correction
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CORRECTION)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Enumerate bonuses
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BONUS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Additional commissions
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Daily commission
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_DAILY)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Monthly commission
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Daily agent commission
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Monthly agent commission
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Interest rate
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_INTEREST)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Canceled buy deal
      if(event.TypeEvent()==TRADE_EVENT_BUY_CANCELLED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Canceled sell deal
      if(event.TypeEvent()==TRADE_EVENT_SELL_CANCELLED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Dividend operations
      if(event.TypeEvent()==TRADE_EVENT_DIVIDENT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Accrual of franked dividend
      if(event.TypeEvent()==TRADE_EVENT_DIVIDENT_FRANKED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Tax charges
      if(event.TypeEvent()==TRADE_EVENT_TAX)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Replenishing account balance
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_REFILL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Withdrawing funds from balance
      if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      
      //--- Pending-Order platziert
      if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_PLASED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Pending-Order entfernt
      if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_REMOVED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Pending order activated by price
      if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Pending order partially activated by price
      if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position opened
      if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position opened partially
      if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed by an opposite one
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_POS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed by StopLoss
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_SL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed by TakeProfit
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_TP)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position reversal by a new deal (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position reversal by activating a pending order (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position reversal by partial market order execution (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position reversal by activating a pending order (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Added volume to a position by a new deal (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Added volume to a position by partial execution of a market order (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Added volume to a position by activating a pending order (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Added volume to a position by partial activation of a pending order (netting)
      if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed partially
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position partially closed by an opposite one
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed partially by StopLoss
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Position closed partially by TakeProfit
      if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- StopLimit order activation
      if(event.TypeEvent()==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order price
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order and StopLoss price
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order and TakeProfit price
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order, StopLoss and TakeProfit price
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order's StopLoss and TakeProfit price
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order's StopLoss
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing order's TakeProfit
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing position's StopLoss and TakeProfit
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing position StopLoss
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS)
        {
         Print(DFUN,event.TypeEventDescription());
        }
      //--- Changing position TakeProfit
      if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT)
        {
         Print(DFUN,event.TypeEventDescription());
        }
     }
  }
//+------------------------------------------------------------------+


Der Einfachheit halber erhalten wir ein Ereignis aus der Liste einfach durch seinen Index (für den Tester wird der Index im Parameter lparam durch die Funktion EventsHandling() übergeben, während bei einem Demo- und realen Konten der Index immer gleich Null ist, da jedes Ereignis als unabhängiges Ereignis an OnChartEvent() und nicht aus der Liste gesendet wird) und zeigen die Beschreibung eines erhaltenen Ereignisses im Journal an.

Sie müssen entscheiden, wie darauf zu regieren ist. Sie können die Behandlung direkt im gleichen Code implementieren oder die Liste der Ereignisflags deklarieren, indem Sie hier die Flags der aufgetretenen Ereignisse setzen, während die eigentliche Behandlung in separaten Funktionen durchgeführt wird.

Dies sind alle Änderungen und Verbesserungen, die notwendig sind, um alle gleichzeitig aufgetretenen Handelsereignisse zu kontrollieren. Die Bibliothek enthält bereits alle notwendigen Dinge für die automatische Korrektur von Fehlern der Handelsauftragsparameter. Im EA sind (vorerst) keine Änderungen erforderlich. Nachdem wir alle Möglichkeiten der Fehlerbehandlung geschaffen haben, werden wir im weiteren Verlauf eine zusätzliche Eingabe einführen, die das Verhalten des EA bei Fehlern angibt.

Kompilieren Sie den EA und starten Sie ihn im Tester. Platzieren Sie mehrere Pending-Orders und entfernen Sie alle in einer einzigen Schleife:


Der EA zeigt vier Ereignisse im Journal an. Diese Ereignisse traten auf, wenn vier Pending-Orders in einer einzigen Schleife entfernt wurden, nachdem Sie auf "Delete pending" geklickt hatten.

Lassen Sie uns nun in den EA-Einstellungen im Strategietester eine größere Losgröße einstellen (z.B. 100.0) und versuchen, einen Pending-Order zu platzieren oder eine Position zu eröffnen:

Nachdem wir versucht haben, eine Pending-Order zu platzieren und eine Position mit dem Volumen vom 100,0 Lots zu eröffnen, haben wir Journalmeldungen erhalten, die über unzureichende Mittel und die Anpassung des Volumens informieren. Die Order wurde gesetzt und die Position danach eröffnet.

Was kommt als Nächstes?

Im nächsten Artikel werden wir die Behandlung von Fehlern, die vom Handelsserver zurückgegeben werden, implementieren.

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

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm
Teil 6. Ereignisse auf Netting-Konten
Teil 7. Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen
Teil 8. Ereignisse von Änderungen von Orders und Positionen
Teil 9. Kompatibilität mit MQL4 - Datenvorbereitung
Teil 10. Kompatibilität mit MQL4 - Ereignisse der Positionseröffnung und Aktivierung von Pending-Orders
Teil 11. Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten
Teil 20. Erstellen und Speichern von Programmressourcen
Teil 21. Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
Teil 22. Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen
Teil 23. Handelsklasse - Basisklasse des Handels, Verifikation der Parameter

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7326

Beigefügte Dateien |
MQL5.zip (3598.41 KB)
MQL4.zip (3598.33 KB)
Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten

Der erste Artikel beschäftigt sich mit der Erstellung eines Toolkits für die Arbeit mit Optimierungsberichten, für den Import aus dem Terminal sowie für die Filterung und Sortierung der erhaltenen Daten. MetaTrader 5 ermöglicht das Laden von Optimierungsergebnissen, unser Ziel ist es jedoch, dem Optimierungsbericht unsere eigenen Daten hinzuzufügen.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIII): Handelsklassen - Verifikation der Parameter Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIII): Handelsklassen - Verifikation der Parameter

In dem Artikel setzen wir die Entwicklung der Handelsklasse fort, indem wir die Kontrolle über fehlerhafte Parameterwerte von Handelsaufträgen und die Audiosignale von Handelsereignissen implementieren.

Die Funktionen des Strategy Builders erweitern Die Funktionen des Strategy Builders erweitern

In den beiden vorangegangenen Artikeln haben wir die Anwendung von Merrill-Mustern auf verschiedene Datentypen diskutiert. Es wurde eine Anwendung entwickelt, um die vorgestellten Ideen zu testen. In diesem Artikel werden wir die Arbeit mit dem Strategy Builder fortsetzen, um seine Effizienz zu verbessern und neue Funktionen und Fähigkeiten zu implementieren.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXV): Behandlung der Fehlermeldungen von Server Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXV): Behandlung der Fehlermeldungen von Server

Nachdem wir einen Handelsauftrag an den Server gesendet haben, müssen wir die Fehlercodes oder das Fehlen von Fehlern überprüfen. In diesem Artikel werden wir die Behandlung von Fehlern, die vom Handelsserver zurückgegeben werden, besprechen und die Erstellung von ausstehenden Handelsanfragen vorbereiten.