Die Sprache MQL5 von Grund auf selbst erlernen - Seite 52

 

Achten Sie auf den Filter nach Symbolen und nach Assistenten in der Positionsschleife. Wenn es keinen Filter gibt, sondern Sie alle offenen Positionen für alle Symbole durchsuchen, ist das schlecht.

Auf den ersten Blick scheint also alles in Ordnung zu sein.

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
 
Andrei Novichkov:

Achten Sie auf den Filter nach Symbolen und nach Assistenten in der Positionsschleife. Wenn es keinen Filter gibt, sondern Sie alle offenen Positionen für alle Symbole durchsuchen, ist das schlecht.

Auf den ersten Blick scheint also alles in Ordnung zu sein.

Vielen Dank, Andrew! Ich verstehe alles über Magic, weil mehrere Positionen für ein Symbol geöffnet werden können, aber ich habe eine andere Frage. Geht der Expert Advisor die offenen Positionen für alle Symbole auf einmal durch, wenn er nicht explizit auf das aktuelle Symbol hingewiesen wird? Und das, obwohl er für ein bestimmtes Währungspaar, z. B. EURUSD, eingestellt ist? Ehrlich gesagt, verstehe ich diesen Punkt nicht ganz.

Mit freundlichen Grüßen, Vladimir.

 
MrBrooklin:

Vielen Dank, Andrey! Ich verstehe alles über Magic, weil mehrere Positionen auf ein Symbol eröffnet werden können, aber ich habe noch eine andere Frage. Wird der EA die offenen Positionen für alle Symbole auf einmal durchgehen, wenn er nicht explizit auf das aktuelle Symbol verwiesen wird? Und das, obwohl er für ein bestimmtes Währungspaar, z. B. EURUSD, eingestellt ist? Ehrlich gesagt, verstehe ich diesen Punkt nicht ganz.

Mit freundlichen Grüßen, Vladimir.


Ja, sie wird in allen offenen Positionen für alle Symbole gesetzt.
Durchgängig für alle offenen Stellen.
Hier ist ein einfacher Blick in das Lehrbuch.

https://book.mql4.com/ru/build/trading
 
MrBrooklin:

Also habe ich auf der Grundlage der gelesenen Literatur einen kurzen Algorithmus zur Erstellung eines Expert Advisors mit der Trailing-Stop-Funktion geschrieben:

  1. Erstellen wir einen Expert Advisor zur Automatisierung des Trailing-Stop-Levels Loss einer bereits offenen Position mit festgelegten Take Profit- und Stop-Levels Loss.
  2. Erstellen Sie im Expert Advisor einen Block von Eingabeparametern mit zwei Parametern: "Trailing Level" und "Trailing Step".
  3. Wenn neue Kurse eintreffen, verarbeiten Sie diese mit der Funktion OnTick( ). Trailing funktioniert nur, wenn ein neuer Tick für das aktuelle Symbol kommt.
  4. Wir erstellen eine Schleife und lassen sie laufen, um alle Positionen zu durchsuchen.
  5. Wenn wir plötzlich keine offenen Positionen finden, kehren wir zur Schleife zurück
  6. Wir aktualisieren die Zitate.
  7. Wenn es eine offene Stelle gibt, fahren wir fort.
  8. Wir definieren die Art einer offenen Position: Kaufen oder Verkaufen.
  9. Wenn es eine offene Kaufpositiongibt , legen wir fest, wo sich der aktuelle Preis im Verhältnis zur offenen Position befindet.
  10. Wenn der aktuelle Kurs höher ist als der Kurs, zu dem die Position eröffnet wurde, prüfen wir, auf welchem Niveau er gestiegen ist.
  11. Wenn der aktuelle Kurs das in den Eingabeparametern definierte "Trailing Level" erreicht hat, verschieben wir denStop Loss auf das Niveau ohne Verlust, das dem Eröffnungskurs derKaufpositionentspricht . Ansonsten tun wir nichts.
  12. Wenn der aktuelle Kurs das Trailing-Stop-Niveau um den Wert überschreitet, der dem Trailing-Stop-Niveau entspricht , wird derStop Loss vom Eröffnungskursniveau der Kaufposition um den Wert verschoben ,der dem Trailing-Stop-Niveau entspricht, und so weiter, bis der Kurs das für diese Position festgelegte Take-Profit-Niveau erreicht .
  13. Wenn der Kurs dreht und das Niveau desStop Losserreicht , wird die Position geschlossen .
  14. Bei einer Verkaufsposition legen wir fest, wo sich der aktuelle Kurs im Verhältnis zur offenen Position befindet .
  15. Wenn der aktuelle Kurs niedriger ist als der Kurs der Positionseröffnung, prüfen wir, auf welches Niveau er gefallen ist.
  16. Wenn der aktuelle Kurs das in den Eingabeparametern angegebene Trailing-Niveau erreicht hat, verschieben wir den Stop Loss auf das Niveau ohne Verlust, das dem Eröffnungskurs derVerkaufspositionentspricht . Ansonsten tun wir nichts.
  17. Wenn der aktuelle Kurs das Trailing-Stop-Niveau um den Wert übersteigt, der dem Trailing-Stop-Niveau entspricht , wird derStop Loss vom Niveau der Eröffnungsverkaufsposition um den Wert verschoben , der dem Trailing-Stop-Niveau entspricht, und so weiter, bis der Kurs das für diese Position festgelegte Take-Profit-Niveau erreicht.
  18. Wenn der Kurs dreht und das Niveau desStop Losserreicht , wird die Position geschlossen .

Bitte überprüfen Sie den Algorithmus und geben Sie mir einige Hinweise darauf, welche Punkte übersehen worden sind.

Mit freundlichen Grüßen, Vladimir.

Die Theorie ist nicht schlecht, jetzt wollen wir uns auf die Praxis konzentrieren. Wird es funktionieren?

 
Aliaksandr Hryshyn:

Gut in der Theorie, jetzt die Praxis. Können Sie das tun?

Ich werde es versuchen. Aber Sie verstehen, dass dies einen ganz anderen Wissensstand erfordert, und den habe ich noch nicht.

Mit freundlichen Grüßen, Vladimir.

 
Aleksey Masterov:

Ja. In allen offenen Posen auf allen Symbolen...
Durchgängig in allen offenen Posen.
Hier ist ein einfacher Suchlauf, der bereits im Lehrbuch enthalten ist.

https://book.mql4.com/ru/build/trading

Ja, Alexey, ich habe diesen Code bereits gesehen. Sie hat die Form einer Include-Datei. Um ehrlich zu sein, habe ich darin nichts über das Symbol gefunden, obwohl ich es mehrmals angesehen habe. Vielleicht habe ich etwas missverstanden oder ich suche einfach nur schlecht.

Mit freundlichen Grüßen, Vladimir.

 

Lassen Sie uns zunächst mit den Funktionen fortfahren.

Wie ich bereits schrieb, gibt es überall Funktionen, man muss sie nur lieben und wissen, wie man sie schreibt. Funktionen, sind unsere kleinen Kämpfer für die Lösung globaler Probleme. Wenn wir Generäle in einer Armee wären, welche Art von Kämpfern würden wir kontrollieren wollen? Hier ist eine grobe Liste:

  • Ein Kämpfer muss einen Befehl eindeutig ausführen. Der durchschnittliche Intelligenzgrad eines Infanteristen ist nicht sehr hoch. Daher ist es besser, für solche Kämpfer klare und einfache Ziele zu setzen: "einen Bunker einnehmen", "eine Zunge bekommen", "eine Brücke verminen".
  • Wenn die Aufgabe schwierig ist, suchen Sie nicht nach einem superschlauen Kämpfer, um sie zu bewältigen. Es ist besser, die Aufgabe in mehrere Teilaufgaben zu unterteilen und zwei oder drei dümmere, aber effizientere Kämpfer zu nehmen. Lassen Sie alle ihre Teilaufgaben ohne Fragen lösen, oder besser noch, lassen Sie sie das Konzept und die Aufgabe als Ganzes nicht kennen. Wenn dann jemand gefangen genommen wird, ist das kein Problem, der ganze Plan wird nicht aufgedeckt.
  • Ein Soldat muss den Befehl unabhängig von der Umgebung befolgen: Schnee, Regen, Paris und Frauen - wenn diese Umgebung keinen Einfluss auf die Ausführung des Befehls hat, dann müssen diese Bedingungen und die äußere Umgebung ignoriert werden.
  • Es kommt vor, dass Aufgaben schwierig sein können. Sie erfordern viele Kämpfer, um sie zu lösen. Nicht jedem Kämpfer kann ein General zugewiesen werden. Stattdessen müssen Sie einen klugen Soldaten mit der Führung mehrerer Kämpfer beauftragen. Diese Gruppe wiederum schließt sich mit denselben in einer Kompanie von Soldaten zusammen und ernennt sie zu einem vorgesetzten Offizier.

Aber wir sind abgelenkt, gehen wir wieder zu den Funktionen über.

Wenn eine Funktion zu viele Probleme löst, ist sie - in Anlehnung an die Analogie - ein sehr cleverer Kämpfer, der, wenn etwas schief geht, das ganze Unternehmen ruinieren könnte. Wenn Sie sich fragen, was eine solche Funktion bewirkt, könnte die Antwort lang sein. Wenn das Ergebnis dieser Funktion plötzlich nicht mehr korrekt ist, wird es sehr schwierig sein, die Fehlerursache herauszufinden (da es viele Aufgaben, viel Code und viele Aufrufe von Unterprozeduren gibt und es schwer zu verstehen ist, wo genau der Fehler liegt).

Wenn eine Funktion montags, mittwochs und sonntags und an den übrigen Tagen je nach unserer "Stimmung" korrekte Ergebnisse berechnet, können wir uns dann auf diese Funktion verlassen? Stellen Sie sich vor, dass die OrderSend-Funktion Positionen nur donnerstags öffnet und dass ein magischer Parameter 13 definiert ist. Und das ist keineswegs Unsinn oder Fantasie. Dieses Verhalten lässt sich mit einem Fingerschnippen einrichten - es genügt, die Funktion von einigen Parametern in der äußeren Umgebung abhängig zu machen.

Angenommen, die Funktion:

double sum(double a, double b)
{
   return a+b;
}

gibt immer die Summe zweier Werte zurück, unabhängig von der äußeren Umgebung. Das heißt, selbst wenn wir diese Funktion in ein anderes Skript oder einen Expert Advisor kopieren, wird sie dort einwandfrei funktionieren. Diese Funktion kann einmal geschrieben werden und durch einfaches Kopieren in vielen unserer Programme verwendet werden. Wir werden uns immer auf sein Ergebnis verlassen können, weil wir wissen, dass sein Funktionieren von nichts abhängt. Solche Funktionen, deren Ergebnis nicht von ihrer Umgebung abhängt, werden als nebenwirkungsfreie oder reine Funktionen bezeichnet. Wenn wir uns bemühen, reine Funktionen zu schreiben, bekommen wir bald eine Menge davon. Das heißt, Sie können sie in einer Datei zusammenfassen und in Ihre neuen Projekte einbinden. Dies wird als Code-Wiederverwendung bezeichnet. Wir machen die Arbeit nicht zweimal. Stattdessen verwenden wir bereits geschriebene Funktionen, die wir kennen und deren Zuverlässigkeit mehr als einmal getestet worden ist.

Betrachten wir nun das Gegenbeispiel:

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

Das Ergebnis scheint dasselbe zu sein, denn c ist immer Null. Oder ist das nicht immer so? Was ist, wenn jemand irgendwo c ändert? Was dann? Was ist, wenn jemand irgendwo die externe Variable c auch verwendet, aber für seine eigenen Zwecke, und er hat eine Variable c von einem anderen Typ, sagen wir string? Die Kombination dieser beiden Funktionen ist nicht mehr möglich (der Compiler erlaubt es nicht, zwei Variablen mit demselben Namen zu deklarieren). Ihre gemeinsamen Abhängigkeiten sind ebenfalls schwer zu lösen. Ich weiß gar nicht, was ich damit anfangen soll. Ich kenne zum Beispiel immer noch keinen zuverlässigen und einfachen Weg, um solche Funktionen zusammenarbeiten zu lassen.

Selbst wenn es keine andere Funktion gibt und nur eine Funktion eine externe Variable liest, ist es nicht so einfach, sie an eine andere Stelle zu kopieren. Wir müssen sowohl diese Funktion als auch ihre Abhängigkeit kopieren. Was aber, wenn wir diese Funktionen in eine gemeinsame Datei kopieren? Wir haben dort 50 oder 100 dieser Funktionen. Und jede von ihnen kopiert mit sich selbst einen Haufen ihrer eigenen abhängigen Variablen. Es entsteht ein Wirrwarr von zusammenhängenden Variablen mit unklaren Funktionen. Aber wozu ist das alles gut? Welche Probleme werden damit gelöst? Warum unnötige Abhängigkeiten schaffen, wenn man in den allermeisten Fällen darauf verzichten kann?

Die Funktionen haben eine weitere überraschende Eigenschaft. Die Funktionen sind selbstbeschreibend. Mit anderen Worten: Sie müssen kein Schema entwerfen, sondern nur gute Namen wählen und den allgemeinen Algorithmus in Funktionen unterteilen. Hier ist ein Beispiel:

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

Ich weiß nicht, was dieser Code bewirkt, denn die Funktionen sind nicht einmal geschrieben. Aber wenn ich es lese, würde es wahrscheinlich bedeuten, dass die erste <erste> anhängige Order mit der Richtung ORDER_TYPE_BUY erfolgreich ausgewählt wird, dann wird sie storniert (die erste Funktion wählt aus, die zweite storniert). Da der Code bei jedem Tick ausgeführt wird, wird jeder einzelne Auftrag früher oder später storniert, unabhängig davon, wie viele ausstehende Aufträge vorliegen. Das bedeutet auch, dass jeder Versuch, einen schwebenden Kaufauftrag zu platzieren, unterdrückt wird - der Auftrag wird sofort gelöscht. Gleichzeitig werden die Verkaufsaufträge ohne Probleme erteilt.

Es gibt nur zwei Codezeilen und zwei Funktionen. Und der Algorithmus ist nicht trivial, und was noch wichtiger ist, er ist zuverlässig.

Wenn wir über MCL sprechen, sollten wir ein wenig mehr über reine Funktionen sagen. Da es sich um eine Anwendungssprache handelt, ist es schwierig, etwas zu schreiben, ohne sich auf die vom Terminal bereitgestellten Daten zu verlassen. Schließlich ist dies die Hauptaufgabe: die richtige Interaktion mit dem Handelsumfeld. Formal ist jede Handelsumgebung veränderbar: Preise, Anzahl der Aufträge, Änderungen des Saldos usw. usw. Es ist daher nicht klar, welche Funktion in einem derart veränderlichen Handelsumfeld zum Tragen kommt. Denn das externe Handelsumfeld kann auch als eine globale Variable betrachtet werden, die sich ständig verändert. Aber wenn wir OrdersTotal() schreiben, erwarten wir nicht, dass diese Funktion immer den gleichen Wert zurückgibt. Stattdessen erwarten wir, dass sie die Anzahl der ausstehenden Aufträge zurückgibt, die natürlich variieren wird. Daher werden wir in MQL die Funktionen als sauber und wiederverwendbar betrachten, auch wenn sie Funktionen einer externen API aufrufen, wie z.B. OrdersTotal(). Es wird unsere angemessene Nachsicht sein.

 
Vasiliy Sokolov:

Lassen Sie uns mit den Funktionen fortfahren...

Vielen Dank, Vasily, für das unbezahlbare Wissen, das du nicht nur mit mir teilst, sondern auch mit den Programmieranfängern, die dieses Thema lesen oder lesen werden!

Mit dem gleichen großen Respekt, Vladimir.

 

Ich lerne weiter die Programmiersprache MQL5. Während es keine ernsthaften Bemerkungen über den Code schreiben Algorithmus der Trailing_Stop Expert Advisor (ich erinnere mich über das Symbol und Magic, ich werde es auf den Algorithmus später hinzufügen!), habe ich Input-Parameter für die EA und schrieb den Code der Schleife, die die Suche nach offenen Positionen beginnt.

Als ich den EA ausführte, sah ich ein Problem - in der Registerkarte "Experten" des Handelsterminals erscheinen bei jedem Tick 2 identische Meldungen "A loop has started", obwohl das Handelsterminal nur einen Chart des Währungspaares EURUSD hat und nur eine Position darauf geöffnet ist. Und diese Nachrichten haben genau den gleichen Zeitpunkt der Ausgabe.

Ich habe bis Mitternacht gekämpft, konnte aber nicht gewinnen. Ich kann nicht verstehen, was das Problem ist.


Der Code des Expert Advisors ist auf Englisch geschrieben, während die Kommentare auf Russisch sind, um den Prozess zu erleichtern. In diesem EA habe ich versucht, wie versprochen, alles so zu beschreiben, dass es für einen Schüler der ersten Klasse einer Programmierschule verständlich ist.

Mit freundlichen Grüßen, Vladimir.

//+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
 
MrBrooklin:


i entspricht der Anzahl der offenen Positionen, so viele Zyklen werden gedruckt

Print("Запущен цикл");
müssen Sie das "="-Zeichen in
   for(i; i>=0; i--)
Warum müssen Sie den Zyklus durchlaufen, wenn die Anzahl der offenen Positionen 0 ist? Der zweite Druck kommt von diesem Null-Aufruf.