English Русский 中文 Español 日本語 Português
preview
MQL5 Handels-Toolkit (Teil 1): Entwicklung einer EX5-Bibliothek zur Positionsverwaltung

MQL5 Handels-Toolkit (Teil 1): Entwicklung einer EX5-Bibliothek zur Positionsverwaltung

MetaTrader 5Beispiele |
307 0
Wanateki Solutions LTD
Kelvin Muturi Muigua

Einführung

Als Softwareentwickler finde ich es oft praktisch und effizient, meine eigenen Code-Bibliotheken oder Toolkits zu erstellen. Das spart mir Zeit, weil ich den Code für allgemeine Aufgaben, die in meinen verschiedenen MQL5-Entwicklungsprojekten anfallen, nicht immer wieder neu schreiben muss. In dieser Artikelserie werden wir eine MQL5-Handelsbibliothek erstellen, die für die Durchführung sich wiederholender Aufgaben in gängigen MQL5-Entwicklungsprojekten verantwortlich ist.

In diesem ersten Artikel werden wir besprechen, was Entwicklerbibliotheken sind, warum sie wichtig sind und welche verschiedenen Arten von Codebibliotheken Sie mit MQL5 erstellen können. Anschließend erstellen wir eine Bibliothek mit MQL5-Funktionen für verschiedene Positionsoperationen als praktisches Beispiel, um Ihr Verständnis dafür zu festigen, wie Sie eine Code-Bibliothek für ein reales Projekt verwenden können.


Was sind Code-Bibliotheken in MQL5?

MQL5 Code-Bibliotheken sind vorgefertigte Code-Funktionen (ex5) oder dynamisch verknüpfte Bibliotheken (DLLs), die wir verwenden können, um den Entwicklungsprozess von Expert Advisors, nutzerdefinierten Indikatoren, Skripten oder Dienstleistungen für die MetaTrader 5-Plattform effizient zu beschleunigen. 

Stellen Sie sich eine Code-Bibliothek wie den Werkzeugkasten eines Mechanikers vor. So wie der Werkzeugkasten eines Mechanikers eine Vielzahl von Werkzeugen (Schraubenschlüssel, Schraubendreher usw.) für bestimmte Aufgaben enthält, so enthält eine Code-Bibliothek vorgefertigte Funktionen, die ähnliche Aufgaben erfüllen. Jede Funktion befasst sich mit einer bestimmten Aufgabe innerhalb Ihres Programms, z. B. dem Öffnen, Schließen oder Ändern von Positionen, dem Senden von Push-Benachrichtigungen, der Datenbankverwaltung usw., ähnlich wie ein Schraubenschlüssel Schrauben anzieht oder ein Schraubenzieher Schrauben dreht.


Arten von Code-Bibliotheken in MQL5

Als MQL5-Entwickler haben Sie mehrere Möglichkeiten, Ihre Code-Bibliothek oder Ihr Toolkit aufzubauen:

  • Funktions-Bibliotheken (ex5): Diese Bibliotheken implementieren einen prozeduralen Kodierungsstil und bieten eine Sammlung von vorgefertigten Funktionen für bestimmte Aufgaben. Außerdem bieten sie den zusätzlichen Sicherheitsvorteil der Codekapselung. Betrachten Sie sie als individuelle Werkzeuge, die jeweils für eine bestimmte Aufgabe konzipiert sind.
  • C++-DLLs von Drittanbietern: Sie können vorgefertigte C++-Bibliotheken als DLLs (Dynamic Linked Libraries) integrieren. Dies erweitert die Möglichkeiten von MQL5, indem es Ihnen ermöglicht, externe Funktionen zu nutzen.

MetaTrader 5 bietet auch zusätzliche Möglichkeiten, Ihr Toolkit zu erweitern:

  • .NET-Bibliotheken: Der MetaEditor bietet eine nahtlose Integration mit .NET-Bibliotheken durch „intelligenten“ Funktionsimport, wodurch die Notwendigkeit für nutzerdefinierte Wrapper entfällt.
  • Python-Sprachmodul: Das neu unterstützte Python-Sprachmodul ermöglicht es Ihnen, Python-Funktionalitäten in Ihren MQL5-Projekten zu nutzen.

Wenn Sie mit C++ vertraut sind, können Sie nutzerdefinierte DLLs erstellen, die sich leicht in Ihre MQL5-Projekte integrieren lassen. Sie können Tools wie Microsoft Visual Studio verwenden, um C++-Quellcodedateien (CPP und H) zu entwickeln, sie in DLLs zu kompilieren und sie dann in MetaEditor zu importieren, um sie mit Ihrem MQL5-Code zu verwenden.

Andere Code-Ressourcen ähnlich wie MQL5-Bibliotheken

  • Klasse/Include (*.mqh): MQL5-Include-Dateien nutzen die objektorientierte Programmierung und bieten vorgefertigte Klassen, die Daten und Funktionen kapseln. Stellen Sie sich diese als komplexere Werkzeuge vor, die Funktionen/Methoden und Datenstrukturen kombinieren. Im Wesentlichen kann eine Klasse oder Struktur nicht exportiert werden, um eine MQL5-Bibliothek (ex5) zu erstellen, aber Sie können Zeiger und Referenzen auf Klassen oder Strukturen in MQL5-Bibliotheksfunktionen verwenden.


Warum müssen Sie eine ex5 MQL5-Bibliothek erstellen oder verwenden?

Die Erstellung eigener Code-Bibliotheken für MQL5-Projekte kann Ihren Entwicklungsprozess wesentlich effizienter gestalten. Diese Bibliotheken, die als .ex5-Dateien gespeichert werden, wirken wie ein persönlicher Werkzeugkasten mit Funktionen, die Sie für bestimmte Aufgaben optimiert haben.

Einfache Wiederverwendbarkeit und modularer Aufbau

Sparen Sie Zeit, indem Sie allgemeine Funktionen nicht jedes Mal neu schreiben müssen, wenn Sie ein neues Projekt beginnen. Mit .ex5-Bibliotheken schreiben Sie den Code einmal, optimieren ihn für beste Leistung, exportieren die Funktionen und importieren sie dann einfach in jedes Projekt. Dieser modulare Ansatz sorgt für einen sauberen und übersichtlichen Code, indem er die Kernfunktionalitäten von der projektspezifischen Logik trennt. Jeder Teil der Bibliothek ist ein Baustein für die Erstellung leistungsstarker Handelssysteme.

Sichere Funktionen mit Verkapselung

Durch die Erstellung von MQL5-Bibliotheken können Sie Ihre Funktionen gemeinsam nutzen, während der Quellcode verborgen bleibt. Durch die Kapselung wird sichergestellt, dass die Details Ihres Codes sicher und für die Nutzer nicht sichtbar sind, während gleichzeitig eine klare Schnittstelle für die Funktionalität bereitgestellt wird. Sie müssen nur die .ex5 Bibliotheksdatei zusammen mit einer klaren Dokumentation der exportierten Funktionsdefinitionen an andere Entwickler weitergeben, damit diese die Bibliotheksfunktionen in ihre Projekte importieren können. Die .ex5-Bibliotheksdatei verbirgt effektiv den Quellcode und die Art und Weise, wie die exportierten Funktionen kodiert sind, wodurch ein sicherer und gekapselter Arbeitsbereich in Ihrem Hauptprojektcode erhalten bleibt.

Einfache Upgrades und langfristiger Nutzen

Wenn neue Sprachfunktionen auf den Markt kommen oder alte veraltet sind, können Sie Ihren Code mit den .ex5-Bibliotheken problemlos aktualisieren. Aktualisieren Sie einfach den Bibliothekscode, stellen Sie ihn erneut bereit, und kompilieren Sie alle Projekte, die ihn verwenden, neu, damit die Änderungen automatisch übernommen werden. Das spart Ihnen viel Zeit und Mühe, besonders bei großen Projekten. Die Bibliothek fungiert als zentrales System für Ihre Codebasis, und eine Aktualisierung oder Änderung wirkt sich auf alle damit verbundenen Projekte aus, was Ihre Arbeit auf lange Sicht effizienter macht.


Wie erstellt man eine ex5-Bibliothek in MQL5?

Alle .ex5-Bibliotheken beginnen als .mq5-Quellcodedateien mit der am Anfang der Datei hinzugefügten Bibliotheksdirektive #property und einer oder mehreren Funktionen, die mit dem speziellen Schlüsselwort export als exportierbar gekennzeichnet sind. Die .mq5-Bibliotheksquellcodedatei wird nach der Kompilierung in eine .ex5-Bibliotheksdatei umgewandelt, die den Quellcode sicher kapselt oder versteckt und ihn für den Import und die Verwendung in anderen MQL5-Projekten bereit macht.

Das Erstellen einer neuen MQL5-Bibliothek ist mit der MetaEditor IDE einfach. Gehen Sie wie folgt vor, um eine neue Bibliotheksquellcodedatei (.mq5) zu erstellen, die die Positionsmanagementfunktionen enthält und später in eine .ex5-Bibliothek kompiliert wird.

Schritt 1: Öffnen Sie die MetaEditor-IDE und starten Sie den „MQL-Assistenten“ über die Schaltfläche „Neu“ (New) im Menü.

Mql5Wizard neue Bibliotheksdatei

Schritt 2: Wählen Sie die Option „Bibliothek“ (Library) und klicken Sie auf „Weiter“ (Next).

Mql5-Assistent Neue Bibliotheksdatei

Schritt 3: Geben Sie im Abschnitt „Allgemeine Eigenschaften der Bibliotheksdatei“ den Ordner und den Namen für Ihre neue Bibliothek „Bibliotheken\Toolkit\PositionsManager“ ein und klicken Sie auf „Fertigstellen“ (Finish), um unsere neue Bibliothek zu erstellen.

Mql5-Assistent für neue Bibliotheksdateien Allgemeine Eigenschaften

Eine MQL5-Bibliothek wird normalerweise in einer Datei mit der Erweiterung „.mq5“ gespeichert. Diese Datei enthält den Quellcode verschiedener Funktionen, die für verschiedene spezifische Aufgaben geschrieben wurden. Code-Bibliotheken werden standardmäßig im Ordner MQL5\Libraries im Installationsverzeichnis von MetaTrader 5 gespeichert. Eine schnelle Möglichkeit, auf den Ordner „Libraries“ zuzugreifen, ist die Verwendung des Navigator-Panels in MetaEditor.

MQL5-Standardbibliotheksordner im MetaEditor-Navigator-Panel

Wir haben jetzt eine neu erstellte, leere MQL5-Bibliotheksdatei PositionsManager.mq5, die die am Anfang die Eigenschaftsrichtlinien und eine auskommentierte Funktion enthält. Denken Sie daran, die neue Datei zu speichern, bevor Sie fortfahren. So sieht unsere neu erstellte Bibliotheksdatei aus:

//+------------------------------------------------------------------+
//|                                             PositionsManager.mq5 |
//|                          Copyright 2024, Wanateki Solutions Ltd. |
//|                                         https://www.wanateki.com |
//+------------------------------------------------------------------+
#property library
#property copyright "Copyright 2024, Wanateki Solutions Ltd."
#property link      "https://www.wanateki.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| My function                                                      |
//+------------------------------------------------------------------+
// int MyCalculator(int value,int value2) export
//   {
//    return(value+value2);
//   }
//+------------------------------------------------------------------+

Bestandteile einer MQL5-Bibliothek mit Quellcode

Eine MQL5 Library-Quellcodedatei (.mq5) besteht aus zwei Hauptkomponenten:

1. Die Bibliotheksrichtlinie #property: Diese Eigenschaftsrichtlinie muss am Anfang der .mq5-Quellcodedatei der Bibliothek eingefügt werden. Die Library-Eigenschaft teilt dem Compiler mit, dass es sich bei der angegebenen Datei um eine Bibliothek handelt. Ein entsprechender Hinweis wird im Header der kompilierten Bibliothek in der.ex5-Datei gespeichert, die aus der Kompilierung der.mq5-Bibliothek resultiert.

#property library

2. Exportierte Funktionen: Das Herzstück der MQL5-Bibliotheken sind die exportierten Funktionen. Dies sind die wichtigsten Komponenten einer Bibliothek, da sie für die Durchführung aller schweren Aufgaben zuständig sind, die von der Bibliothek ausgeführt werden müssen. Eine exportierte MQL5-Funktion ist ähnlich wie eine gewöhnliche MQL5-Funktion, außer dass sie mit dem nachgestellten Modifikator export deklariert wird, sodass sie nach der Kompilierung importiert und in anderen MQL5-Programmen verwendet werden kann. Der Exportmodifikator weist den Compiler an, die angegebene Funktion in die Tabelle der ex5-Funktionen aufzunehmen , die von dieser Bibliotheksdatei exportiert werden. Nur Funktionen, die mit dem Export-Modifikator deklariert sind, sind von anderen mql5-Programmen aus zugänglich und auffindbar, wo sie nach dem Importieren mit dem speziellen #import Direktive aufgerufen werden können.

//+------------------------------------------------------------------+
//| Example of an exported function with the export postmodifier         |
//+------------------------------------------------------------------+
int ExportedFunction(int a, int b) export
  {
   return(a + b);
  }
//+------------------------------------------------------------------+

Von MQL5-Bibliotheken wird nicht erwartet oder verlangt, dass sie Standard-Ereignisse wie Expert Advisors, nutzerdefinierte Indikatoren oder Skripte direkt ausführen. Das bedeutet, dass sie keine der Standardfunktionen wie OnInit(), OnDeinit() oder OnTick() hat.


MQL5-Funktionsbibliothek für die Bestandsverwaltung

Das Positionsmanagement ist eine grundlegende Aufgabe für jeden in der Entwicklung befindlichen Expert Advisor. Diese grundlegenden Operationen bilden den Kern eines jeden algorithmischen Handelssystems. Um sich wiederholende Kodierungen zu vermeiden, sollten MQL5-Entwickler Bibliotheken verwenden, um Positionen effizient zu verwalten. Dadurch wird sichergestellt, dass die Entwickler nicht für jeden Expert Advisor denselben Positionsverwaltungscode neu schreiben müssen.

In diesem Abschnitt fügen wir unserer neu erstellten Datei PositionsManager.mq5 etwas Code hinzu, um eine Positionsmanagement-Bibliothek mit MQL5 zu erstellen. Diese Bibliothek wird alle positionsbezogenen Vorgänge abwickeln. Durch den Import in den Code Ihres Expert Advisors können Sie Positionen effektiv ausführen und verwalten und Ihre Codebasis sauber und übersichtlich halten.


Globale Variablen


Handelsanfragen in MQL5 werden durch eine spezielle vordefinierte Struktur dargestellt, die als MqlTradeRequest. Diese Struktur enthält alle notwendigen Felder, die für die Ausführung von Handelsgeschäften erforderlich sind, und stellt sicher, dass alle erforderlichen Auftragsanforderungsdaten in einer einzigen Datenstruktur gebündelt sind. Wenn eine Handelsanfrage bearbeitet wird, wird das Ergebnis einer anderen vordefinierten Struktur namens MqlTradeResult zugewiesen. MqlTradeResult ist dafür verantwortlich, uns detaillierte Informationen über das Ergebnis der Handelsanfrage zu liefern, einschließlich der Angabe, ob die Anfrage erfolgreich war und aller relevanten Daten bezüglich der Ausführung des Handels.

Da wir diese beiden speziellen Datenstrukturen in den meisten unserer Funktionen verwenden werden, sollten wir sie zunächst als globale Variablen deklarieren, damit sie in der gesamten Bibliothek verfügbar sind.

//---Global variables
//-------------------------------
//-- Trade operations request and result data structures
MqlTradeRequest tradeRequest;
MqlTradeResult  tradeResult;
//-------------------------------


Funktionen für das Fehler-Management von Positionen


Die erste Funktion in unserer Bibliothek wird eine Funktion für die Fehlerverwaltung sein. Beim Eröffnen, Ändern und Schließen von Positionen mit MQL5 treten häufig verschiedene Arten von Fehlern auf, die entweder einen Abbruch des Vorgangs oder ein erneutes Senden der Positionsverwaltungsanfrage an den Handelsserver erfordern. Die Erstellung einer speziellen Funktion zur Überwachung und Empfehlung der richtigen Maßnahmen ist eine Notwendigkeit für eine ordnungsgemäß kodierte und optimierte Bibliothek.

Bevor wir die Fehlerbehandlungsfunktion erstellen können, müssen wir die verschiedenen Fehlercodes der MQL5-Positionsverwaltung verstehen, auf die wir stoßen könnten. In der nachstehenden Tabelle sind einige der Fehlercodes vom Handelsserver und zur Laufzeit hervorgehoben, die wir bei der Verwaltung verschiedener Positionsoperationen erkennen und beheben müssen. Eine vollständige Liste aller Fehlercodes und Warnungen finden Sie in der MQL5-Dokumentation.

Code Code-Konstante  Beschreibung
Ergriffene Maßnahmen Art des Codes 
10004  TRADE_RETCODE_REQUOTE Neuer Preis Senden Sie den Auftrag erneut ab. Rückgabecode des Handelsservers (RETCODE)
10008  TRADE_RETCODE_PLACED Auftrag erteilt Keine Maßnahmen zu ergreifen. Operationen waren erfolgreich. Rückgabecode des Handelsservers (RETCODE)
10009  TRADE_RETCODE_DONE Auftrag abgeschlossen Keine Maßnahmen zu ergreifen. Operationen abgeschlossen.
Rückgabecode des Handelsservers (RETCODE)
10013  TRADE_RETCODE_INVALID Ungültige Auftrag Beenden Sie das erneute Senden der Auftragsinitialisierungsanfrage und aktualisieren Sie die Auftragsdetails. Rückgabecode des Handelsservers (RETCODE)
10014  TRADE_RETCODE_INVALID_VOLUME Ungültiges Volumen im Auftrag Beenden Sie das erneute Senden der Auftragsinitialisierungsanfrage und aktualisieren Sie die Auftragsdetails. Rückgabecode des Handelsservers (RETCODE)
10016  TRADE_RETCODE_INVALID_STOPS Ungültige Stopps im Auftrag Beenden Sie das erneute Senden der Auftragsinitialisierungsanfrage und aktualisieren Sie die Auftragsdetails. Rückgabecode des Handelsservers (RETCODE)
10017  TRADE_RETCODE_TRADE_DISABLED Handel ist deaktiviert Beenden Sie alle Handelsoperationen und senden sie keine weiteren Aufträge. Rückgabecode des Handelsservers (RETCODE)
10018  TRADE_RETCODE_MARKET_CLOSED Der Markt ist geschlossen Stoppen Sie das erneute Senden der Aufträgen. Rückgabecode des Handelsservers (RETCODE)
10019  TRADE_RETCODE_NO_MONEY Es ist nicht genug Geld vorhanden, um den Auftrag zu erfüllen Beenden Sie das erneute Senden des Auftrags und aktualisieren Sie die Auftragsdetails. Rückgabecode des Handelsservers (RETCODE)
10026  TRADE_RETCODE_SERVER_DISABLES_AT Autotrading vom Server deaktiviert Das Autotrading ist auf dem Server nicht erlaubt. Stoppen Sie das erneute Senden der Aufträgen. Rückgabecode des Handelsservers (RETCODE)
10027  TRADE_RETCODE_CLIENT_DISABLES_AT Autotrading auf dem Client-Terminal deaktiviert. Das Client-Terminal hat den EA-Handel deaktiviert. Stoppen Sie das erneute Senden der Aufträgen. Rückgabecode des Handelsservers (RETCODE)
10034  TRADE_RETCODE_LIMIT_VOLUME Das Limit des Volumens für Order und Positionen für dieses Symbol ist erreicht. Stoppen Sie das erneute Senden der Aufträgen. Rückgabecode des Handelsservers (RETCODE)
10011  TRADE_RETCODE_ERROR Fehler bei der Bearbeitung des Auftrags. Senden Sie den Auftrag zur Auftragsinitialisierung immer wieder neu. Rückgabecode des Handelsservers (RETCODE)
10012  TRADE_RETCODE_TIMEOUT Auftrag wurde durch ein Timeout abgebrochen. Unterbrechen Sie die Ausführung für ein paar Millisekunden und senden Sie dann erneut den Auftrag. Rückgabecode des Handelsservers (RETCODE)
10015  TRADE_RETCODE_INVALID_PRICE Ungültiger Preis in der Anfrage. Aktualisieren Sie den Auftragspreis und senden Sie den Auftrag erneut. Rückgabecode des Handelsservers (RETCODE)
10020  TRADE_RETCODE_PRICE_CHANGED Preise haben sich geändert. Aktualisieren Sie den Auftragspreis und senden Sie den Auftrag erneut. Rückgabecode des Handelsservers (RETCODE)
10021  TRADE_RETCODE_PRICE_OFF Es liegen keine Angebote zur Bearbeitung des Auftrags vor. Unterbrechen Sie die Ausführung für einige Millisekunden und senden Sie dann den Auftrag erneut. Rückgabecode des Handelsservers (RETCODE)
10024  TRADE_RETCODE_TOO_MANY_REQUESTS Zu häufige Anfragen. Unterbrechen Sie die Ausführung für einige Sekunden und senden Sie dann den Auftrag erneut. Rückgabecode des Handelsservers (RETCODE)
 10031  TRADE_RETCODE_CONNECTION Keine Verbindung mit dem Handelsserver. Unterbrechen Sie die Ausführung für ein paar Millisekunden und senden Sie dann den Auftrag erneut. Rückgabecode des Handelsservers (RETCODE)
 0  ERR_SUCCESS Die Operation wurde erfolgreich abgeschlossen Beenden Sie ein erneutes Senden des Auftrags. Gesendeter Auftrag ist OK.   Laufzeit-Fehlercode
 4752  ERR_TRADE_DISABLED Handel durch Expert Advisors verboten Stoppen Sie das erneute Senden der Aufträgen. Laufzeit-Fehlercode
 4753  ERR_TRADE_POSITION_NOT_FOUND Position wurde nicht gefunden. Stoppen Sie das erneute Senden des Handelsauftrags.
Laufzeit-Fehlercode
 4754  ERR_TRADE_ORDER_NOT_FOUND Order wurde nicht gefunden. Stoppen Sie das erneute Senden der Order.
Laufzeit-Fehlercode
 4755  ERR_TRADE_DEAL_NOT_FOUND Deal nicht gefunden Stoppen Sie das erneute Senden der Order.
Laufzeit-Fehlercode


Erstellen wir unsere erste Funktion in der Bibliothek, um die oben genannten Fehler zu bearbeiten. Die Fehlerverarbeitungsfunktion mit dem Namen ErrorAdvisor() ist vom Typ boolean, d. h. sie gibt je nach Art des aufgetretenen Fehlers True oder False zurück. Sie nimmt zwei Argumente entgegen, um die Datenverarbeitung zu erleichtern:

  1. callingFunc (string): Dieser Parameter speichert den Namen oder die Kennung der Funktion, die ErrorAdvisor() aufruft.
  2. Symbol (Zeichenfolge): Dieser Parameter speichert den Symbolnamen des zu bearbeitenden Assets.
  3. tradeServerErrorCode (ganze Zahl): Dieser Parameter speichert die Art des aufgetretenen Fehlers.

Wenn der Fehler behebbar und nicht kritisch ist, gibt die Funktion ErrorAdvisor() True zurück. Dies zeigt der aufrufenden Funktion an, dass der Auftrag noch nicht ausgeführt wurde und sie die Auftragsanfrage erneut senden sollte. Wenn ErrorAdvisor() den Wert False zurückgibt, bedeutet dies, dass die aufrufende Funktion keine weiteren Auftragsanforderungen mehr senden sollte, da der Auftrag bereits erfolgreich ausgeführt wurde oder ein kritischer, nicht behebbarer Fehler aufgetreten ist.

Denken Sie daran, den nachgestellten Modifikator export vor die öffnende geschweifte Klammer der Funktion zu setzen, um anzuzeigen, dass die Funktion zu einer Bibliothek gehört und für die Verwendung in anderen MQL5-Programmen vorgesehen ist.

//------------------------------------------------------------------+
// ErrorAdvisor(): Error analysis and processing function.          |
// Returns true if order opening failed and order can be re-sent    |
// Returns false if the error is critical and can not be executed   |
//------------------------------------------------------------------+
bool ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode)  export
  {
//-- place the function body here
  }

Beginnen wir mit der Deklaration und Initialisierung einer Integer-Variablen, die den aktuellen Laufzeitfehler speichert. Benennen Sie die Ganzzahl runtimeErrorCode und rufen Sie die Funktion GetLastError() auf, um den letzten Laufzeitfehler zu speichern. Wir werden diese Variable verwenden, um jeden Laufzeitfehler zu verarbeiten, der im zweiten verschachtelten Schalteroperator auftreten könnte.

//-- save the current runtime error code
   int runtimeErrorCode = GetLastError();

Wir werden die Rückgabefehler des Handelsservers (Retcode) und die Laufzeitfehler mit Hilfe von einem verschachtelten switch überprüfen und verarbeiten. Dies ist praktisch, da wir so den Fehlertyp schnell identifizieren, eine Beschreibung für den Nutzer im ExpertAdvisor-Protokoll ausgeben und die aufrufende Funktion anweisen können, wie sie weiter vorgehen soll. Sie werden feststellen, dass ich die Fehler in zwei Kategorien eingeteilt habe:

  1. Fehler, die den Abschluss oder die Nichtausführung eines Auftrags anzeigen: Diese Fehler geben False zurück und weisen die aufrufende Funktion an, keinen weiteren Auftrag mehr zu senden.
  2. Fehler, die auf unvollständige Aufträge hinweisen: Durch diese Fehler wird True zurückgegeben und weisen so die aufrufende Funktion an, die Aufträge erneut zu senden.

Der erste Switch-Operator befasst sich mit den Rückgabecodes des Handelsservers und der zweite verschachtelte Switch-Operator mit den Laufzeitfehlercodes. Dieser Ansatz minimiert den Code der Funktion, indem er sequenzielle Prüfungen für jeden Fehlercode oder jede Warnung vermeidet.

Lassen Sie uns nun die erste switch-Anweisung codieren, um den aufgetretenen tradeServerErrorCode zu untersuchen und zu sehen, welche Art von Fehler der Handelsserver gemeldet hat.

switch(tradeServerErrorCode)//-- check for trade server errors
     {
        //--- Cases to scan different retcodes/server return codes
     }

Innerhalb der switch-Anweisung fügen wir Fälle für verschiedene vom Handelsserver zurückgegebene Fehlercodes hinzu. Hier sind einige von ihnen:

Requote (Code 10004): Das bedeutet, dass sich der Preis geändert hat, nachdem der Nutzer seinen Auftrag gesendet hat. In diesem Fall sollten wir eine Meldung ins Protokoll schreiben, mit der Funktion Sleep() ein paar Millisekunden warten und dann der aufrufenden Funktion mitteilen, dass sie erneut versuchen soll, den Auftrag abzusenden.

case 10004:
    Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Requote!");
    Sleep(10);
    return(true);    //--- Exit the function and retry opening the order again

Auftrag erfolgreich platziert (Code 10008): Wenn dieser Code zurückgegeben wird, ist alles gut gelaufen, und der Auftrag wurde platziert. Wir können eine Meldung in das Protokoll drucken, die besagt, dass der Auftrag erfolgreich war, und dann der aufrufenden Funktion mitteilen, dass sie nicht mehr versuchen soll, den Auftrag erneut zu senden.

case 10008:
    Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Order placed!");
    return(false);    //--- success - order placed ok. exit function

Wir werden ähnliche Fälle für andere Handelsserver-Fehler (Codes 10009, 10011, 10012 usw.) hinzufügen und dabei der gleichen Logik folgen, d.h. eine Meldung für das ExpertAdvisor-Protokoll ausdrucken, ggf. ein wenig warten und die aufrufende Funktion anweisen, ob sie das Senden der Handelsanfrage erneut versuchen soll.

Wenn die switch-Anweisung für Handelsserverfehler keine Übereinstimmung findet, bedeutet dies, dass der Fehler möglicherweise ein Laufzeitfehler ist und nur durch die Erstellung einer weiteren switch-Anweisung gefunden werden kann, die den aktuellen Laufzeitfehler abfragt, der von der Funktion GetLastError() zurückgegeben wird. Um dieses Problem zu lösen, verwenden wir eine verschachtelte switch-Anweisung im Standardabschnitt der vorherigen switch-Anweisung, um den Wert des runtimeErrorCode zu untersuchen, den wir zuvor gespeichert haben.

default:
  switch(runtimeErrorCode)//-- check for runtime errors
    //-- Add cases for different runtime errors here
  }
}

Wie wiederholen den gleichen Prozess wie bei den Fehlern des Handelsservers und fügen Fälle für verschiedene Laufzeitfehler hinzu:

Keine Fehler (Code 0): Das bedeutet, dass aus der Sicht unseres Programms alles gut funktioniert hat. Wir können eine Meldung im Protokoll ausgeben, die besagt, dass keine Fehler aufgetreten sind, und dann der aufrufenden Funktion sagen, dass sie den Versuch abbrechen soll.

case 0:
    Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") The operation completed successfully!");
    ResetLastError(); //--- reset error cache
    return(false);    //--- Exit the function and stop trying to open order

Wir werden weiterhin ähnliche Fälle für andere Laufzeitfehler (Codes 4752, 4753, 4754 usw.) hinzufügen und dabei der gleichen Logik folgen, d.h. eine Meldung für das ExpertAdvisor-Fehlerprotokoll ausgeben und der aufrufenden Funktion mitteilen, ob sie die Handelsanfrage stoppen oder fortsetzen soll.

Da wir nur die wichtigsten Fehlercodes berücksichtigt haben, die sich auf die Auftragsausführung auswirken können, und nicht alle möglicherweise vorhandenen Fehlercodes gescannt oder verarbeitet haben, kann es vorkommen, dass wir auf einen Fehler stoßen, der derzeit nicht verarbeitet oder in unserem aktuellen Code berücksichtigt wird. In diesem Fall geben wir eine Meldung in das Protokoll ein, die angibt, dass ein unbekannter (anderer) Fehler aufgetreten ist, weist den Server-Rückgabecode und den aufgetretenen Laufzeitfehler aus und weist die aufrufende Funktion an, keinen Versuch zu unternehmen, den Auftrag zu senden.

default: //--- All other error codes
    Print(symbol, " - ", callingFunc, " *OTHER* Error occurred \r\nTrade Server RetCode: ", tradeServerErrorCode, ", Runtime Error Code = ", runtimeErrorCode);
    ResetLastError(); //--- reset error cache
    return(false);    //--- Exit the function and stop trying to open order
    break;

Hier ist die komplette Fehlerverwaltungsfunktion ErrorAdvisor() mit allen Codesegmenten fertiggestellt:

bool ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode)  export
  {
//-- save the current runtime error code
   int runtimeErrorCode = GetLastError();

   switch(tradeServerErrorCode)//-- check for trade server errors
     {
      case 10004:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Requote!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again
      case 10008:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Order placed!");
         return(false);    //--- success - order placed ok. exit function

      case 10009:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request completed!");
         return(false);    //--- success - order placed ok. exit function

      case 10011:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request processing error!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10012:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Request canceled by timeout!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      case 10015:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Invalid price in the request!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10020:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Prices changed!");
         Sleep(10);
         return(true);    //--- Exit the function and retry opening the order again

      case 10021:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") There are no quotes to process the request!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      case 10024:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") Too frequent requests!");
         Sleep(1000);
         return(true);    //--- Exit the function and retry opening the order again

      case 10031:
         Print(symbol, " - ", callingFunc, " ->(TradeServer_Code: ", tradeServerErrorCode, ") No connection with the trade server!");
         Sleep(100);
         return(true);    //--- Exit the function and retry opening the order again

      default:
         switch(runtimeErrorCode)//-- check for runtime errors
            case 0:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") The operation completed successfully!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4752:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Trading by Expert Advisors prohibited!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4753:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Position not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4754:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Order not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            case 4755:
               Print(symbol, " - ", callingFunc, " ->(Runtime_Code: ", runtimeErrorCode, ") Deal not found!");
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order

            default: //--- All other error codes
               Print(symbol, " - ", callingFunc, " *OTHER* Error occurred \r\nTrade Server RetCode: ", tradeServerErrorCode, ", Runtime Error Code = ", runtimeErrorCode);
               ResetLastError(); //--- reset error cache
               return(false);    //--- Exit the function and stop trying to open order
               break;
           }
     }
  }


Funktion Handelserlaubnis


Diese Funktion prüft, ob der Handel im Handelsterminal derzeit erlaubt ist. Es berücksichtigt die Autorisierung durch den Nutzer, den Handelsserver und den Makler. Die Funktion wird aufgerufen, bevor Positionsoperationen oder Auftragsanforderungen zur Verarbeitung an den Handelsserver gesendet werden.

Wir nennen die Funktion TradingIsAllowed() und geben ihr den Rückgabetyp Boolean. Wenn der Handel erlaubt und aktiviert ist, wird der boolesche Wert „True“ zurückgegeben und der boolesche Wert „False“, wenn der automatische Handel deaktiviert oder nicht erlaubt ist. Sie hat keine Parameter oder Argumente und enthält das nachstehende Codesegment:

//+-----------------------------------------------------------------------+
//| TradingIsAllowed() verifies whether auto-trading is currently allowed |                                                                 |
//+-----------------------------------------------------------------------+
bool TradingIsAllowed() export
  {
   if(
      !IsStopped() &&
      MQLInfoInteger(MQL_TRADE_ALLOWED) && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) &&
      AccountInfoInteger(ACCOUNT_TRADE_ALLOWED) && AccountInfoInteger(ACCOUNT_TRADE_EXPERT)
   )
     {
      return(true);//-- trading is allowed, exit and return true
     }
   return(false);//-- trading is not allowed, exit and return false
  }


Funktion zum Protokollieren und Drucken der Auftragsdetails


Dies ist eine einfache Funktion zum Protokollieren und Drucken der Eigenschaften der verschiedenen Handelsoperationen oder Positionseröffnungsaufträge. Es bietet eine einfache Möglichkeit für den Nutzer eines ExpertAdvisors, sich über den Status der ExpertAdvisor-Operationen über die Registerkarte „MetaTrader 5 EA Log“ auf dem Laufenden zu halten. Sie werden feststellen, dass diese Funktion nicht exportiert wird und daher nur über andere exportierte Funktionen zugänglich ist, die sie ausdrücklich aufrufen oder ausführen. Wir geben ihr den Namen PrintOrderDetails() und legen fest, dass sie keine Daten zurückgibt, sodass es sich um eine Funktion des Typs void handelt, und dass sie eine String-Variable als Eingabeparameter oder Argument annimmt.

//+-----------------------------------------------------------------------+
//| PrintOrderDetails() prints the order details for the EA log           |
//+-----------------------------------------------------------------------+
void PrintOrderDetails(string header)
  {
   string orderDescription;
//-- Print the order details
   orderDescription += "_______________________________________________________________________________________\r\n";
   orderDescription += "--> "  + tradeRequest.symbol + " " + EnumToString(tradeRequest.type) + " " + header +
                       " <--\r\n";
   orderDescription += "Order ticket: " + (string)tradeRequest.order + "\r\n";
   orderDescription += "Volume: " + StringFormat("%G", tradeRequest.volume) + "\r\n";
   orderDescription += "Price: " + StringFormat("%G", tradeRequest.price) + "\r\n";
   orderDescription += "Stop Loss: " + StringFormat("%G", tradeRequest.sl) + "\r\n";
   orderDescription += "Take Profit: " + StringFormat("%G", tradeRequest.tp) + "\r\n";
   orderDescription += "Comment: " + tradeRequest.comment + "\r\n";
   orderDescription += "Magic Number: " + StringFormat("%d", tradeRequest.magic) + "\r\n";
   orderDescription += "Order filling: " + EnumToString(tradeRequest.type_filling)+ "\r\n";
   orderDescription += "Deviation points: " + StringFormat("%G", tradeRequest.deviation) + "\r\n";
   orderDescription += "RETCODE: " + (string)(tradeResult.retcode) + "\r\n";
   orderDescription += "Runtime Code: " + (string)(GetLastError()) + "\r\n";
   orderDescription += "---";
   Print(orderDescription);
  }


Position Eröffnungsfunktionen


Wir werden diese Funktionen in zwei Kategorien einteilen:

  1. Die Funktion OpenBuyPositions(): Diese exportierbare Funktion ist, wie der Name schon sagt, für die Eröffnung neuer Kaufpositionen zuständig.
  2. Die Funktion OpenSellPositions(): Diese exportierbare Funktion übernimmt, wie der Name schon sagt, die alleinige Aufgabe, neue Verkaufspositionen zu eröffnen.

Die Funktion OpenBuyPositions()

Diese Funktion ist vom Typ bool und gibt den Wert true zurück, wenn sie erfolgreich eine neue Kaufposition eröffnen konnte und false, wenn es nicht möglich war, eine neue Kaufposition zu eröffnen. Er nimmt sechs Parameter oder Argumente entgegen:

  1. ulong magicNumber: Wird verwendet, um die magische Zahl des ExpertAdvisors zu speichern, um die Änderung oder Beendigung von Positionen durch einfaches Filtern von Positionen zu erleichtern.
  2. String-Symbol: Speichert den Namen des Symbols oder Handelsinstruments, für das die Anfrage ausgeführt wird.
  3. double lotSize: Speichert das Volumen oder die Menge der zu eröffnenden Kaufposition.
  4. int sl: Speichert den Stop-Loss-Wert in Punkten/Pips der Kaufposition.
  5. int tp: Speichert den Take-Profit-Wert in Punkten/Pips der Kaufposition.
  6. string positionComment: Wird verwendet, um den Kommentar der Kaufposition zu speichern oder aufzubewahren.

Wir beginnen mit der Codierung der Funktionsdefinition. Beachten Sie, dass wir den „Post-Modifikator“ export vor die öffnende geschweifte Klammer der Funktion gesetzt haben, um den Compiler anzuweisen, diese Funktion zur Verwendung in anderen MQL5-Projekten, die diese Bibliothek implementieren, exportierbar zu machen.

bool OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
        //--- Function body
  }

Bevor wir versuchen, einen Auftrag zu senden, sollten wir prüfen, ob unser Expert Advisor überhaupt handeln darf. Dies geschieht durch den Aufruf der Funktion TradingIsAllowed(), die wir zuvor erstellt haben. Die Funktion TradingIsAllowed() sucht nach verschiedenen Einstellungen und Berechtigungen, um sicherzustellen, dass der automatische Handel aktiviert ist.

Wenn TradingIsAllowed() den Wert false zurückgibt, bedeutet dies, dass der Handel deaktiviert ist und unser ExpertAdvisor keine Aufträge öffnen kann. In diesem Fall geben wir auch in dieser Funktion sofort false zurück und beenden sie, ohne einen neuen Kaufauftrag zu eröffnen.

//-- first check if the ea is allowed to trade
if(!TradingIsAllowed())
  {
   return(false); //--- algo trading is disabled, exit function
  }

Der nächste Schritt ist die Vorbereitung auf die Auftragsanfrage, indem alle Datenreste von früheren Handelsversuchen gelöscht werden. Dazu verwenden wir die Funktion ZeroMemory() für die beiden globalen Handelsdatenstrukturen, die wir zu Beginn unserer Datei erstellt haben: tradeRequest und tradeResult. Darin werden die Details des zu eröffnenden Kaufauftrags bzw. die vom Handelsserver zurückgegebenen Ergebnisse gespeichert.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

Legen wir nun die Parameter für die Eröffnung einer Kaufposition in der Datenstrukturvariablen tradeRequest fest:

  • tradeRequest.type: Setzen Sie für einen Kaufauftrag diesen Wert auf ORDER_TYPE_BUY.
  • tradeRequest.action: Für eine neue Position setzen Sie dies auf TRADE_ACTION_DEAL.
  • tradeRequest.magic: Wir weisen hier die als Argument angegebene magicNumber zu. Dies hilft bei der Identifizierung von Aufträgen, die von unserem ExpertAdvisor eröffnet wurden.
  • tradeRequest.symbol: Weisen Sie hier das als Argument angegebene Symbol zu, das das zu handelnde Währungspaar angibt.
  • tradeRequest.tp und tradeRequest.sl: Wir setzen diese Werte vorerst auf 0, da wir uns später mit Take-Profit (TP) und Stop-Loss (SL) beschäftigen werden.
  • tradeRequest.comment: Weisen Sie hier den als Argument angegebenen positionComment zu, mit dem ein Textkommentar zum Auftrag hinzugefügt werden kann.
  • tradeRequest.deviation: Stellen Sie dies so ein, dass eine Abweichung von bis zum Doppelten des aktuellen Spreads für das gehandelte Symbol zulässig ist. Dies gibt der Plattform eine gewisse Flexibilität bei der Suche nach einem passenden Auftragskurs und begrenzt die Anzahl der Preisanpassungen eines Auftrags.

//-- initialize the parameters to open a buy position
   tradeRequest.type = ORDER_TYPE_BUY;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

Die Normalisierung des Auftragsvolumens oder der Losgröße ist ein sehr wichtiger Schritt. Dazu passen wir das variable Argument oder den Parameter lotSize an, um sicherzustellen, dass es innerhalb des zulässigen Bereichs für das gewählte Symbol liegt. Wir werden es folgendermaßen anpassen:

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

Als Nächstes werden wir die Funktion ResetLastError() verwenden, um alle vorherigen Laufzeitfehlercodes von der Plattform zu löschen. Dadurch wird sichergestellt, dass wir später einen genauen Fehlercode von der Funktion ErrorAdvisor() erhalten.

//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function
   ResetLastError();

Jetzt geben wir eine Schleife ein, die bis zu 100 Mal versucht, den Auftrag zu platzieren (mit einem Maximum von 101 Iterationen). Diese Schleife dient als Ausfallsicherung für den Fall, dass der Auftrag aufgrund vorübergehender Marktschwankungen oder aus anderen Gründen nicht beim ersten Versuch eröffnet werden kann.

for(int loop = 0; loop <= 100; loop++) //-- try opening the order untill it is successful (100 max tries)
   {
    //--- Place the order request code to open a new buy position
   }

Die erste Aufgabe in der for-Schleife besteht darin, den Eröffnungskurs des Auftrags bei jeder Iteration zu aktualisieren, wenn eine Preisanpassung des Auftrags erfolgt. Wir verwenden SymbolInfoDouble(symbol, SYMBOL_ASK), um den aktuellen Verkaufspreis für das Symbol zu ermitteln und ihn tradeRequest.price zuzuweisen. Dadurch wird sichergestellt, dass unsere Auftrag den aktuellen Marktpreis widerspiegelt.

//--- update order opening price on each iteration
   tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_ASK);

Als Nächstes werden die Take-Profit- und Stop-Loss bei jeder Iteration aktualisiert, damit sie mit dem aktualisierten Einstiegskurs übereinstimmen. Gleichzeitig werden die Werte normalisiert, um sicherzustellen, dass sie den erforderlichen Genauigkeitsanforderungen entsprechen, und dann der Datenstruktur tradeRequest.tp und tradeRequest.tp zugewiesen.

//-- set the take profit and stop loss on each iteration
   if(tp > 0)
     {
      tradeRequest.tp = NormalizeDouble(tradeRequest.price + (tp * _Point), _Digits);
     }
   if(sl > 0)
     {
      tradeRequest.sl = NormalizeDouble(tradeRequest.price - (sl * _Point), _Digits);
     }

Anschließend senden wir den Auftrag zur Ausführung an den Handelsserver. Für diese Aufgabe verwenden wir die Funktion OrderSend() und übergeben die vorbereitete TradeRequest und die leere Variable TradeResult als Argumente oder Parameter. Diese Funktion versucht, den Auftrag auf der Grundlage der in tradeRequest gespeicherten Spezifikationen zu öffnen. Die Ergebnisse, einschließlich der Erfolgs- oder Fehlercodes, werden nach Abschluss der Ausführung in der Variablen tradeResult gespeichert.

Mit der Anweisung if für die Funktion OrderSend() können wir prüfen und bestätigen, ob der Auftrag erfolgreich war oder nicht. Wenn OrderSend() true zurückgibt, bedeutet dies, dass der Auftrag erfolgreich gesendet wurde, und wenn es false zurückgibt, bedeutet dies, dass der Auftrag nicht akzeptiert wurde.

Wir rufen dann unsere zuvor kodierte Funktion PrintOrderDetails() mit der Meldung „Sent OK“ auf, um diese Informationen im Expert Advisor-Protokoll zu protokollieren.

Überprüfen Sie auch den tradeResult.retcode, um die erfolgreiche Auftragsausführung zu bestätigen. Um den Erfolg anzuzeigen, geben wir in der Funktion OpenBuyPosition() true zurück, und verwenden break, um die Schleife ganz zu verlassen. Wenn OrderSend() false zurückgibt (was bedeutet, dass die Bestellanforderung fehlgeschlagen ist), bedeutet dies, dass ein Problem aufgetreten ist. Wir rufen PrintOrderDetails() mit der Meldung „Sending Failed“ auf, um diese Informationen zu protokollieren. Wir werden auch eine Fehlermeldung ausgeben, um die verschiedenen Fehlercodes hervorzuheben, auf die wir gestoßen sind, und false von der Funktion OpenBuyPosition() zurückgeben, um einen Fehler anzuzeigen, und break verwenden, um die Schleife zu verlassen.

//--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " BUY POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit the function
            break; //--- success - order placed ok. exit the for loop
           }
        }
      else //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, ": ", symbol, " ERROR opening a BUY POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit the function
            break; //-- exit the for loop
           }
        }

Hier ist die OpenBuyPosition() mit allen Codesegmenten in ihrer richtigen Reihenfolge:

//-------------------------------------------------------------------+
// OpenBuyPosition(): Function to open a new buy entry order.        |
//+------------------------------------------------------------------+
bool OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
//-- first check if the ea is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to open a buy position
   tradeRequest.type = ORDER_TYPE_BUY;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function
   ResetLastError();

   for(int loop = 0; loop <= 100; loop++) //-- try opening the order untill it is successful (100 max tries)
     {
      //--- update order opening price on each iteration
      tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_ASK);

      //-- set the take profit and stop loss on each iteration
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price + (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price - (sl * _Point), _Digits);
        }

      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " BUY POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit the function
            break; //--- success - order placed ok. exit the for loop
           }
        }
      else //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, ": ", symbol, " ERROR opening a BUY POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit the function
            break; //-- exit the for loop
           }
        }
     }
   return(false);
  }


Die Funktion OpenSellPositios()

Diese Funktion ist der Funktion OpenBuyPosition(), die wir oben fertig kodiert haben, sehr ähnlich und folgt den gleichen Prozeduren, mit ein paar Unterschieden, wie z.B. der Art der zu verarbeitenden Order. Die Funktion OpenSellPosition() dient dazu, eine neue Verkaufsposition zu eröffnen. Sie enthält eine for-Schleife, die im Falle eines Fehlschlags mehrere Versuche unternimmt und so die Erfolgsquote deutlich erhöht, solange sie mit gültigen Parametern für die Handelsanfrage versorgt wird. Hier ist der Code der Funktion OpenSellPosition():

//-------------------------------------------------------------------+
// OpenSellPosition(): Function to open a new sell entry order.      |
//+------------------------------------------------------------------+
bool OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment) export
  {
//-- first check if the ea is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to open a sell position
   tradeRequest.type = ORDER_TYPE_SELL;
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.magic = magicNumber;
   tradeRequest.symbol = symbol;
   tradeRequest.tp = 0;
   tradeRequest.sl = 0;
   tradeRequest.comment = positionComment;
   tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * 2;

//-- set and moderate the lot size or volume
   lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));  //-- Verify that volume is not less than allowed minimum
   lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));  //-- Verify that volume is not more than allowed maximum
   lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); //-- Round down to nearest volume step
   tradeRequest.volume = lotSize;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try opening the order (101 max) times untill it is successful
     {
      //--- update order opening price on each iteration
      tradeRequest.price = SymbolInfoDouble(symbol, SYMBOL_BID);

      //-- set the take profit and stop loss on each iteration
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price - (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price + (sl * _Point), _Digits);
        }

      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Print the order details
         PrintOrderDetails("Sent OK");

         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print("CONFIRMED: Successfully openend a ", symbol, " SELL POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
            PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- Print the order details
         PrintOrderDetails("Sending Failed");

         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped())
           {
            Print(symbol, " ERROR opening a SELL POSITION at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume);
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }


Funktion zur Änderung von Stop-Loss und Take-Profit


Unsere nächste Funktion in der Bibliothek heißt SetSlTpByTicket() und ist für die Änderung der Stop-Loss (SL) und Take-Profit (TP) für eine bestehende offene Position verantwortlich, wobei das Ticket der Position als Filtermechanismus verwendet wird. Sie nimmt die Ticketnummer der Position, den gewünschten SL in Pips (Punkten) und den gewünschten TP in Pips (Punkten) als Argumente und versucht, den SL und TP der Position auf dem Handelsserver zu aktualisieren. Die Funktion gibt einen booleschen Wert zurück (true oder false). Wenn die Stop-Loss und Take-Profit für die Position erfolgreich geändert wurden, wird true zurückgegeben, und wenn es nicht möglich ist, die Stop-Loss oder Take-Profit erfolgreich zu setzen oder zu ändern, wird false zurückgegeben.

Es folgt eine Aufschlüsselung der Argumente oder Parameter der Funktion SetSlTpByTicket():

  1. ulong positionTicket: Dies ist eine eindeutige Kennung für die Position, die wir ändern werden.
  2. int sl: Dies ist der gewünschte Stop-Loss in Pips (Punkten) vom Eröffnungskurs.
  3. int tp: Dies ist der gewünschte Take-Profit in Pips (Punkten) vom Eröffnungskurs.

Denken Sie daran, in der Funktionsdefinition den nachgestellten Modifikator export zu verwenden, damit unsere Bibliothek von außen darauf zugreifen kann.

bool SetSlTpByTicket(ulong positionTicket, int sl, int tp) export
  {
//-- Function body
  }

Genau wie bei unseren anderen Funktionen oben, prüfen wir zunächst mit der Funktion TradingIsAllowed(), ob der Handel für unseren ExpertAdvisor erlaubt ist. Wenn der Handel deaktiviert ist, wird die Funktion beendet und gibt false zurück.

//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

Bevor wir die mit dem Argument positionTicket angegebene Position auswählen, setzen wir zunächst die Laufzeit-Fehlercode-Systemvariable zurück, um später eine genaue Fehlerantwort für die Funktion ErrorAdvisor() zu erhalten. Wenn die Auswahl erfolgreich ist, wird eine Meldung gedruckt, die anzeigt, dass die Position ausgewählt ist, und gibt uns grünes Licht für den Zugriff auf die Eigenschaften der Position. Schlägt die Auswahl fehl, wird eine Fehlermeldung zusammen mit dem mit GetLastError() ermittelten Fehlercode ausgegeben. Die Funktion beendet sich dann, indem sie false zurückgibt.

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to set SLTP.");
     }
   else
     {
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

Nachdem wir die Position ausgewählt haben, müssen wir einige Details über sie sammeln und speichern. Wir werden diese Informationen für Berechnungen und spätere Referenzen verwenden.

//-- create variables to store the calculated tp and sl prices to send to the trade server
   double tpPrice = 0.0, slPrice = 0.0;
   double newTpPrice = 0.0, newSlPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

Wir benötigen auch einige symbolbezogene Informationen:

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

Berechnen wir nun den neuen Stop-Loss (SL) und Take-Profit (TP) auf der Grundlage des Eröffnungskurses der Position, des gewünschten SL/TP in Pips (Punkten) und des Positionstyps (Kauf oder Verkauf). Wir werden diese ersten Berechnungen speichern, bevor wir sie validieren:

//--Save the non-validated tp and sl prices
   if(positionType == POSITION_TYPE_BUY) //-- Calculate and store the non-validated sl and tp prices
     {
      newSlPrice = entryPrice - (sl * symbolPoint);
      newTpPrice = entryPrice + (tp * symbolPoint);
     }
   else  //-- SELL POSITION
     {
      newSlPrice = entryPrice + (sl * symbolPoint);
      newTpPrice = entryPrice - (tp * symbolPoint);
     }

Als Nächstes drucken wir eine Zusammenfassung der zuvor erfassten Positionsdaten:

//-- Print position properties before modification
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " SLTP Modification Details" +
   " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Proposed SL: " + (string)newSlPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New Proposed TP: " + (string)newTpPrice + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

Da die vom Bibliotheksnutzer angegebenen Werte für SL und TP möglicherweise nicht direkt von der Funktion OrderSend() verwendet werden können. Bevor wir fortfahren können, müssen wir eine einfache Validierung ihrer Werte vornehmen:

//-- validate the sl and tp to a proper double that can be used in the OrderSend() function
   if(sl == 0)
     {
      slPrice = 0.0;
     }
   if(tp == 0)
     {
      tpPrice = 0.0;
     }

Nun müssen wir eine komplexere Validierung auf der Grundlage der zuvor gespeicherten Symboldetails durchführen. Wir werden die Validierungslogik in zwei Gruppen gruppieren, eine für die Kaufpositionen und eine für die Verkaufspositionen. Die Validierung von SL- und TP basiert auf dem aktuellen Preis des Symbols, den Mindestbeschränkungen des Symbol-Stop-Levels und dem Spread des Symbols.

Wenn ein angegebener TP- oder SL-Kurs ungültig ist und außerhalb des erforderlichen Bereichs liegt, wird der ursprüngliche TP- oder SL-Kurs beibehalten, und es wird eine Meldung gedruckt, die erklärt, warum die Änderung fehlgeschlagen ist. Nachdem wir die Validierung der SL- und TP-Werte abgeschlossen haben, drucken wir eine weitere Zusammenfassung aus, um die bestätigten und verifizierten Werte als Referenz zu protokollieren:

//--- Check if the sl and tp are valid in relation to the current price and set the tpPrice
   if(positionType == POSITION_TYPE_BUY)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice + (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice - (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP is valid
         tp > 0 &&
         (
            newTpPrice <= entryPrice + (spread * symbolPoint) ||
            newTpPrice <= positionPriceCurrent ||
            (
               newTpPrice - entryPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent > entryPrice && newTpPrice - positionPriceCurrent < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice >= positionPriceCurrent ||
            entryPrice - newSlPrice < symbolStopLevel * symbolPoint ||
            positionPriceCurrent - newSlPrice < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }
   if(positionType == POSITION_TYPE_SELL)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice - (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice + (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP price is valid
         tp > 0 &&
         (
            newTpPrice >= entryPrice - (spread * symbolPoint) ||
            newTpPrice >= positionPriceCurrent ||
            (
               entryPrice - newTpPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent < entryPrice && positionPriceCurrent - newTpPrice < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice <= positionPriceCurrent ||
            newSlPrice - entryPrice < symbolStopLevel * symbolPoint ||
            newSlPrice - positionPriceCurrent < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }

//-- Print verified position properties before modification
   positionProperties = "---\r\n";
   positionProperties += "--> Validated and Confirmed SL and TP: <--\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + ", Price Current: " + StringFormat("%G", positionPriceCurrent) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New TP: " + (string)tpPrice + "\r\n";
   Print(positionProperties);

Nun, da wir die validierten SL- und TP-Werte haben, ist es an der Zeit, eine Anfrage an den Handelsserver zu senden, um sie zu ändern. Wir verwenden die Funktion ZeroMemory() , um die Strukturen von TradeRequest und TradeResult zu löschen und sicherzustellen, dass sie keine Restdaten aus früheren Operationen enthalten. Dann initialisieren wir die Struktur tradeRequest mit den folgenden Informationen:

  • action: Auf TRADE_ACTION_SLTP setzen, um die Änderung von Stop-Loss und Take-Profit anzuzeigen.
  • position: Wird auf das positionTicket gesetzt, um die Position anzugeben, an der wir arbeiten.
  • symbol: Wird auf das positionSymbol gesetzt, um das Symbol für diese Position zu identifizieren.
  • sl: Wird auf den slPrice gesetzt, der den validierten Stop-Loss-Wert enthält.
  • tp: Wird auf den tpPrice gesetzt, der den validierten Take-Profit-Wert enthält.

Als Nächstes rufen wir die Funktion ResetLastError() auf, um alle zuvor intern gespeicherten Fehlercodes zu löschen. Dadurch wird sichergestellt, dass wir während des Auftragsvorgangs genaue Fehlercodes erhalten.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

Wir sind bereit, den Auftrag an den Handelsserver zu senden. Die Erfahrung hat mich jedoch gelehrt, dass die Auftragsausführung gelegentlich aufgrund vorübergehender Netzwerkprobleme oder einer Serverüberlastung fehlschlagen kann. Das bedeutet, dass wir einen intelligenten Weg finden müssen, um das Senden des Auftrags mit Wiederholungen zu handhaben. Um dieses Problem zu lösen, verwenden wir eine for-Schleife, die bis zu 101 Mal durchlaufen wird (Schleife <= 100). Dieser Wiederholungsmechanismus hilft bei der Bewältigung möglicher vorübergehender Fehler während der Auftragsausführung.

Innerhalb der for-Schleife verwenden wir OrderSend(), um die in tradeRequest enthaltene Auftragsanfrage zu senden und das Ergebnis in tradeResult zu speichern. Wenn OrderSend() den Wert true zurückgibt, bedeutet dies, dass die SL- und TP-Preise erfolgreich geändert wurden und der Auftrag ohne Probleme abgeschlossen wurde.

Wir werden auch eine abschließende Bestätigung vornehmen, indem wir den tradeResult.retcode auf bestimmte Codes (10008 oder 10009) überprüfen, die eine erfolgreiche SL/TP-Änderung für diese Position anzeigen. Wenn die Codes übereinstimmen, drucken wir eine Bestätigungsnachricht mit Details wie dem Positionsticket, dem Symbol und den Rückgabecodes. Anschließend verwenden wir return(true), um die Funktion erfolgreich zu beenden. Die break-Anweisung beendet die Schleife nur, um ganz sicher zu sein, dass wir die for-Schleife verlassen, um unnötige Iterationen zu vermeiden. Wenn OrderSend() false zurückgibt oder der Retcode nicht mit den Erfolgscodes übereinstimmt, liegt ein Fehler vor.

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = tpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }

Hier ist dieFunktion SetSlTpByTicket() mit allen Codesegmenten und ihrer richtigen Reihenfolge. Vergewissern Sie sich, dass Ihre Funktion alle Bestandteile des unten stehenden Codes enthält:

bool SetSlTpByTicket(ulong positionTicket, int sl, int tp) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to set SLTP.");
     }
   else
     {
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//-- create variables to store the calculated tp and sl prices to send to the trade server
   double tpPrice = 0.0, slPrice = 0.0;
   double newTpPrice = 0.0, newSlPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

//--Save the non-validated tp and sl prices
   if(positionType == POSITION_TYPE_BUY) //-- Calculate and store the non-validated sl and tp prices
     {
      newSlPrice = entryPrice - (sl * symbolPoint);
      newTpPrice = entryPrice + (tp * symbolPoint);
     }
   else  //-- SELL POSITION
     {
      newSlPrice = entryPrice + (sl * symbolPoint);
      newTpPrice = entryPrice - (tp * symbolPoint);
     }

//-- Print position properties before modification
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " SLTP Modification Details" +
   " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Proposed SL: " + (string)newSlPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New Proposed TP: " + (string)newTpPrice + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

//-- validate the sl and tp to a proper double that can be used in the OrderSend() function
   if(sl == 0)
     {
      slPrice = 0.0;
     }
   if(tp == 0)
     {
      tpPrice = 0.0;
     }

//--- Check if the sl and tp are valid in relation to the current price and set the tpPrice
   if(positionType == POSITION_TYPE_BUY)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice + (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice - (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP is valid
         tp > 0 &&
         (
            newTpPrice <= entryPrice + (spread * symbolPoint) ||
            newTpPrice <= positionPriceCurrent ||
            (
               newTpPrice - entryPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent > entryPrice && newTpPrice - positionPriceCurrent < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice >= positionPriceCurrent ||
            entryPrice - newSlPrice < symbolStopLevel * symbolPoint ||
            positionPriceCurrent - newSlPrice < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }
   if(positionType == POSITION_TYPE_SELL)
     {
      //-- calculate the new sl and tp prices
      newTpPrice = 0.0;
      newSlPrice = 0.0;
      if(tp > 0)
        {
         newTpPrice = entryPrice - (tp * symbolPoint);
        }
      if(sl > 0)
        {
         newSlPrice = entryPrice + (sl * symbolPoint);
        }

      //-- save the new sl and tp prices incase they don't change afte validation below
      tpPrice = newTpPrice;
      slPrice = newSlPrice;

      if( //-- Check if specified TP price is valid
         tp > 0 &&
         (
            newTpPrice >= entryPrice - (spread * symbolPoint) ||
            newTpPrice >= positionPriceCurrent ||
            (
               entryPrice - newTpPrice < symbolStopLevel * symbolPoint ||
               (positionPriceCurrent < entryPrice && positionPriceCurrent - newTpPrice < symbolStopLevel * symbolPoint)
            )
         )
      )
        {
         //-- Specified TP price is invalid, don't modify the TP
         Print(
            "Specified proposed ", positionSymbol,
            " TP Price at ", newTpPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent TP at ", StringFormat("%G", currentPositionTpPrice), " will not be changed!"
         );
         tpPrice = currentPositionTpPrice;
        }

      if( //-- Check if specified SL price is valid
         sl > 0 &&
         (
            newSlPrice <= positionPriceCurrent ||
            newSlPrice - entryPrice < symbolStopLevel * symbolPoint ||
            newSlPrice - positionPriceCurrent < symbolStopLevel * symbolPoint
         )
      )
        {
         //-- Specified SL price is invalid, don't modify the SL
         Print(
            "Specified proposed ", positionSymbol,
            " SL Price at ", newSlPrice,
            " is invalid since current ", positionSymbol, " price is at ", positionPriceCurrent,
            "\r\nCurrent SL at ", StringFormat("%G", currentPositionSlPrice), " will not be changed!"
         );
         slPrice = currentPositionSlPrice;
        }
     }

//-- Print verified position properties before modification
   positionProperties = "---\r\n";
   positionProperties += "--> Validated and Confirmed SL and TP: <--\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + ", Price Current: " + StringFormat("%G", positionPriceCurrent) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "   -> New TP: " + (string)tpPrice + "\r\n";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = tpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR modified SLTP for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }


Die Funktion, eine Position zu schließen.


Diese Funktion wird ClosePositionByTicket() heißen und einen strukturierten Ansatz verwenden, um sicherzustellen, dass wir Positionen auf der Grundlage ihrer Ticketnummern effektiv schließen können. Sie nimmt die Ticketnummer der Position als Argument. Sie prüft, ob der Handel erlaubt ist, wählt die Position mithilfe des bereitgestellten Tickets aus, ruft die Eigenschaften der Position ab und druckt sie aus, bereitet eine Handelsanfrage vor und versucht, die Position zu schließen, während sie alle auftretenden Fehler behandelt.

Zunächst definieren wir die Funktion und legen fest, dass sie einen booleschen Wert (true oder false) zurückgibt und einen Parameter als Argument akzeptiert.

bool ClosePositionByTicket(ulong positionTicket) export
  {
//--- Function body
  }

Anschließend prüfen wir, ob der Expert Advisor für den Handel zugelassen ist.

//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

Als Nächstes setzen wir alle vorherigen Fehler mit der Funktion ResetLastError() zurück und wählen dann die Position anhand der angegebenen Ticketnummer aus. Wenn die Position ausgewählt ist, wird eine Meldung zur Bestätigung der Auswahl gedruckt, und wenn die Auswahl fehlschlägt, wird eine Fehlermeldung ausgegeben und die Funktion durch Rückgabe von false beendet.

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to be closed.");
     }
   else
     {
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

Sobald die Position erfolgreich ausgewählt wurde, speichern wir ihre Eigenschaften und drucken sie aus.

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double positionVolume = PositionGetDouble(POSITION_VOLUME);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Print position properties before closing it
   string positionProperties;
   positionProperties += "-- "  + positionSymbol + " " + EnumToString(positionType) + " Details" +
   " -------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", PositionGetDouble(POSITION_VOLUME)) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", PositionGetDouble(POSITION_PRICE_OPEN)) + "\r\n";
   positionProperties += "SL: " + StringFormat("%G", PositionGetDouble(POSITION_SL)) + "\r\n";
   positionProperties += "TP: " + StringFormat("%G", PositionGetDouble(POSITION_TP)) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "_______________________________________________________________________________________";
   Print(positionProperties);

Als Nächstes setzen wir die Werte der Datenstrukturen tradeRequest und tradeResult zurück, indem wir die Funktion ZeroMemory() verwenden, um alle vorherigen Daten in diesen Strukturen zu löschen. Anschließend initialisieren wir die Parameter der Handelsanforderung, um die Position zu schließen, indem wir die Handelsaktion auf TRADE_ACTION_DEAL setzen, um eine Handelsbeendigung, das Positionsticket, das Symbol, das Volumen und die Preisabweichung anzugeben.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the trade reqiest parameters to close the position
   tradeRequest.action = TRADE_ACTION_DEAL; //-- Trade operation type for closing a position
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.volume = positionVolume;
   tradeRequest.deviation = SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD) * 2;

Nun müssen wir den Schlusskurs der Position und die Auftragsart bestimmen, je nachdem, ob es sich um einen Kauf oder Verkauf handelt.

//--- Set the price and order type of the position being closed
   if(positionType == POSITION_TYPE_BUY)
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_BID);
      tradeRequest.type = ORDER_TYPE_SELL;
     }
   else//--- For sell type positions
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_ASK);
      tradeRequest.type = ORDER_TYPE_BUY;
     }

Schließlich setzen wir alle vorherigen Fehler mit der Funktion ResetLastError() zurück und versuchen dann, die Position zu schließen, indem wir die Handelsanfrage senden. Wir verwenden eine for-Schleife, um die Anforderung zum Schließen der Position mehrmals zu senden, um sicherzustellen, dass die Position auch bei einer schwachen Internetverbindung oder beim Auftreten unkritischer Fehler geschlossen wird. Wenn der Auftrag erfolgreich gesendet und ausgeführt wurde (Rückgabecodes 10008 oder 10009), wird eine Erfolgsmeldung gedruckt und true zurückgegeben. Wenn der Auftrag fehlschlägt, wird die Funktion ErrorAdvisor() aufgerufen, um den Fehler zu behandeln. Wenn die Funktion ErrorAdvisor() einen kritischen Fehler anzeigt oder der ExpertAdvisor gestoppt wird, wird eine Fehlermeldung ausgegeben und false zurückgegeben, um anzuzeigen, dass die Positionsschließung fehlgeschlagen ist.

ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try closing the position 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("Successfully closed position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- position closing request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("ERROR closing position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }

Achten Sie darauf, dass alle obigen Codesegmente in der folgenden Reihenfolge angeordnet sind. Hier sind alle Codesegmente der Funktion ClosePositionByTicket() in der richtigen Reihenfolge aufgeführt:

bool ClosePositionByTicket(ulong positionTicket) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(PositionSelectByTicket(positionTicket))
     {
      //---Position selected
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Position with ticket:", positionTicket, " selected and ready to be closed.");
     }
   else
     {
      Print("...........................................................................................");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double positionVolume = PositionGetDouble(POSITION_VOLUME);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Print position properties before closing it
   string positionProperties;
   positionProperties += "-- "  + positionSymbol + " " + EnumToString(positionType) + " Details" +
   " -------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", PositionGetDouble(POSITION_VOLUME)) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", PositionGetDouble(POSITION_PRICE_OPEN)) + "\r\n";
   positionProperties += "SL: " + StringFormat("%G", PositionGetDouble(POSITION_SL)) + "\r\n";
   positionProperties += "TP: " + StringFormat("%G", PositionGetDouble(POSITION_TP)) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "_______________________________________________________________________________________";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the trade reqiest parameters to close the position
   tradeRequest.action = TRADE_ACTION_DEAL; //-- Trade operation type for closing a position
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.volume = positionVolume;
   tradeRequest.deviation = SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD) * 2;

//--- Set the price and order type of the position being closed
   if(positionType == POSITION_TYPE_BUY)
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_BID);
      tradeRequest.type = ORDER_TYPE_SELL;
     }
   else//--- For sell type positions
     {
      tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_ASK);
      tradeRequest.type = ORDER_TYPE_BUY;
     }

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try closing the position 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("Successfully closed position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- position closing request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            Print(__FUNCTION__, "_________________________________________________________________________");
            PrintFormat("ERROR closing position #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }

Speichern und kompilieren Sie die Quellcodedatei PositionsManager.mq5 der Bibliothek, und Sie werden feststellen, dass eine neue Bibliotheksdatei PositionsManager.ex5 im Verzeichnis Libraries\Toolkit\ Ordner, in dem wir unsere Bibliothek gespeichert haben.


Schlussfolgerung

Inzwischen haben Sie ein solides Verständnis der MQL5 ex5-Bibliotheken und ihres Erstellungsprozesses erworben. Im nächsten Artikel werden wir unsere Positionsverwaltungsbibliothek um zusätzliche Funktionalitäten für verschiedene Positionsverwaltungsaufgaben erweitern und dann anhand verschiedener praktischer Beispiele demonstrieren, wie ex5-Bibliotheken in jedes MQL5-Projekt implementiert werden können. Die Quellcodedatei PositionsManager.mq5 der Bibliothek, die alle oben beschriebenen Funktionen enthält, finden Sie am Ende dieses Artikels.


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/14822

Beigefügte Dateien |
Datenwissenschaft und maschinelles Lernen (Teil 23): Warum schneiden LightGBM und XGBoost besser ab als viele KI-Modelle? Datenwissenschaft und maschinelles Lernen (Teil 23): Warum schneiden LightGBM und XGBoost besser ab als viele KI-Modelle?
Diese fortschrittlichen gradient-boosted Entscheidungsbaumtechniken bieten eine überragende Leistung und Flexibilität, wodurch sie sich ideal für die Finanzmodellierung und den algorithmischen Handel eignen. Erfahren Sie, wie Sie diese Tools nutzen können, um Ihre Handelsstrategien zu optimieren, die Vorhersagegenauigkeit zu verbessern und sich einen Wettbewerbsvorteil auf den Finanzmärkten zu verschaffen.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 21): Testen mit Wirtschaftskalenderdaten MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 21): Testen mit Wirtschaftskalenderdaten
Die Daten des Wirtschaftskalenders sind standardmäßig nicht für das Testen mit Expert Advisors im Strategy Tester verfügbar. Wir sehen uns an, wie Datenbanken helfen können, diese Einschränkung zu umgehen. In diesem Artikel untersuchen wir, wie SQLite-Datenbanken verwendet werden können, um Wirtschaftskalender-Nachrichten zu archivieren, sodass assistentengestützte Expert Advisors diese nutzen können, um Handelssignale zu generieren.
Integration von Hidden-Markov-Modellen in MetaTrader 5 Integration von Hidden-Markov-Modellen in MetaTrader 5
In diesem Artikel zeigen wir, wie mit Python trainierte Hidden Markov Modelle in MetaTrader 5 Anwendungen integriert werden können. Hidden-Markov-Modelle sind ein leistungsfähiges statistisches Instrument zur Modellierung von Zeitreihendaten, bei denen das modellierte System durch nicht beobachtbare (verborgene) Zustände gekennzeichnet ist. Eine grundlegende Prämisse von HMMs ist, dass die Wahrscheinlichkeit, sich zu einem bestimmten Zeitpunkt in einem bestimmten Zustand zu befinden, vom Zustand des Prozesses im vorherigen Zeitfenster abhängt.
Bill Williams Strategie mit und ohne andere Indikatoren und Vorhersagen Bill Williams Strategie mit und ohne andere Indikatoren und Vorhersagen
In diesem Artikel werden wir einen Blick auf eine der berühmten Strategien von Bill Williams werfen, sie diskutieren und versuchen, die Strategie mit anderen Indikatoren und mit Vorhersagen zu verbessern.