English
preview
Beherrschung der Marktdynamik: Erstellen eines Expert Advisors (EA) mit Unterstützungs- und Widerstandsstrategie

Beherrschung der Marktdynamik: Erstellen eines Expert Advisors (EA) mit Unterstützungs- und Widerstandsstrategie

MetaTrader 5Handel | 1 August 2024, 12:47
64 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In diesem Artikel werden wir die Forex-Handelsstrategie „Support and Resistance“ (Unterstützung und Widerstand) im Kontext des reinen Price-Action-Handels und die Erstellung eines darauf basierenden Expert Advisors (EA) erörtern. Wir werden die Definition, die Typen, die Beschreibung und die Entwicklung der Strategie in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5) untersuchen. Wir werden nicht nur die Theorie hinter der Strategie erörtern, sondern auch die entsprechenden Konzepte, die Analyse und Identifizierung sowie die Visualisierung auf dem Chart, was sie zu einem nützlichen Werkzeug für Händler macht, die lernen wollen, ihre Fähigkeit zur Vorhersage von Marktbewegungen zu verbessern, bessere Entscheidungen zu treffen und schließlich im Risikomanagement geübt zu werden. Mit Hilfe der folgenden Themen werden wir das oben genannte Ziel erreichen:

  1. Definition von Unterstützung und Widerstand
  2. Beschreibung der Unterstützung und des Widerstands
  3. Arten von Unterstützungen und Widerständen
  4. Beschreibung der Handelsstrategie
  5. Entwurf der Handelsstrategie
  6. Implementierung in MetaQuotes Language 5 (MQL5)
  7. Ergebnisse im Strategietester
  8. Schlussfolgerung

Auf dieser Reise werden wir ausgiebig die MetaQuotes Language 5 (MQL5) als Basis für unsere integrierte Entwicklungsumgebung (IDE) verwenden und die Dateien auf dem MetaTrader 5 (MT5) Handelsterminal ausführen. Daher ist es von größter Bedeutung, dass die oben genannten Versionen vorhanden sind. Fangen wir also an.


Definition von Unterstützung und Widerstand

Die Unterstützungs- und Widerstandsstrategie für den Devisenhandel ist ein fundamentales Analyseinstrument, das von vielen Devisenhändlern verwendet wird, um Preisniveaus zu analysieren und zu identifizieren, bei denen der Markt wahrscheinlich entweder eine Pause einlegt oder einen Umschwung vollzieht. Technisch gesehen neigen diese Niveaus dazu, von den historischen Kursen abgelehnt zu werden, wodurch sie im Laufe der Zeit an Bedeutung gewinnen, da der Kurs einmal an diesen Niveaus innehält und sich umkehrt. Wenn diese Niveaus konstruiert sind und der Kurs in der Regel mehrmals von den Schlüsselniveaus abprallt, deutet dies auf ein starkes Kauf- oder Verkaufsinteresse hin. 


Beschreibung von Unterstützung und Widerstand

Die Beschreibung der Strategie von Unterstützung und Widerstand dreht sich um ihre Anwendung in Handelsszenarien. Unterstützungsniveaus zeigen in der Regel eine untere Grenze an, die der Preis nur schwer durchbrechen kann, was auf eine Konzentration der Nachfrage hindeutet, während Widerstandsniveaus eine obere Grenze darstellen, die auf eine Konzentration des Angebots hindeutet. Käufer steigen in der Regel bei Unterstützungsniveaus in den Markt ein, und die Preise werden wahrscheinlich steigen, sodass es für Händler ein guter Zeitpunkt ist, über Käufe oder Long-Positionen nachzudenken. Andererseits treten die Verkäufer bei Widerstandsniveaus auf den Plan, und die Kurse können fallen, was den Händlern die Möglichkeit gibt, zu verkaufen oder Leerverkäufe zu tätigen. Hier ist eine Visualisierung dessen, was wir meinen.

S & R

Der Markteintritt ist immer dynamisch und hängt vom eigenen Einstellungen und den eigenen Vorlieben ab, aber es gibt zwei grundsätzliche Möglichkeiten, mit den Werten zu handeln. Einige Händler ziehen es vor, mit das Abprallen zu handeln, indem sie kaufen, wenn der Kurs auf Unterstützungsniveaus fällt, und verkaufen, wenn der Kurs auf Widerstandsniveaus steigt. Umgekehrt ziehen es andere Händler vor, den Durchbruch zu handeln, indem sie kaufen, wenn der Kurs durch Widerstandsniveaus nach oben bricht, und verkaufen, wenn der Kurs durch Unterstützungsniveaus nach unten bricht. Daher kann man entweder den Ausbruch ausblenden oder den Ausbruch handeln.


Arten von Unterstützungen und Widerständen

Es gibt vier Arten von Unterstützungs- und Widerstandsniveaus.

  • Gerundete Zahlen für Unterstützungs- und Widerstandsniveaus: Diese Niveaus entstehen, wenn der Preis von einem Niveau desselben Preises abprallt, was zu einem horizontalen Preiskanal führt. So könnten die Tiefststände eines Marktes beispielsweise bei 0,65432, 0,65435 und 0,65437 liegen. In der Regel handelt es sich dabei um die gleichen Niveaus mit einem vernachlässigbaren Neigungswinkel, was auf eine Konzentration der Preisnachfrage hinweist.
  • Trendlinienkanal-Unterstützung und -Nachfragelevel: Durch Aufwärts- oder Abwärtstrendlinien gebildete Umkehrpunkte schaffen Angebots- und Nachfragezonen, auf die die Kurse zu reagieren pflegen.

TRENDLINE S&R

  • Fibonacci-Unterstützungs- und Widerstandsniveaus: Fibonacci wird von Händlern verwendet, um Preisumkehrzonen zu identifizieren, und diese Zonen fungieren in der Regel als Angebots- und Nachfragezonen für die Unterstützungs- und Widerstandsebenen.
  • Unterstützungs- und Widerstandsniveaus der Indikatoren: Technische Indikatoren wie gleitende Durchschnitte geben Zonen an, in denen die Kurse tendenziell reagieren, um Drehpunkte für Unterstützungs- und Widerstandsniveaus zu schaffen.

MA IND S&R


Beschreibung der Handelsstrategie

Wie wir gesehen haben, gibt es verschiedene Arten von Unterstützungs- und Widerstandsstrategien in der Forex-Welt. Für diesen Artikel werden wir uns für den Typ der horizontalen, gerundeten Zahlen entscheiden und damit arbeiten, und dann kann das gleiche Konzept für die anderen Typen verwendet und angepasst werden.

Zunächst werden wir das Chart analysieren und die Koordinaten der Unterstützung und des Widerstands ermitteln. Sobald die entsprechenden Koordinaten bestimmt sind, zeichnen wir die Niveaus in das Chart ein. Auch hier haben wir gesehen, dass jeder Händler zwei Möglichkeiten hat, um mit den Niveaus zu handeln, d.h. entweder ignorieren (fade) oder den Ausbruch (break) handeln. In unserem Fall werden wir den Ausbruch ignorieren. Wir werden Kaufpositionen eröffnen, wenn wir die Unterstützungen durchbrechen, und Verkaufspositionen eröffnen, wenn wir die Widerstandsniveaus durchbrechen. So einfach ist das.


Entwurf der Handelsstrategie

Um das von uns vermittelte Konzept leicht zu verstehen, sollten wir es in einer Blaupause visualisieren.

  • Widerstandsniveau:

WIDERSTANDS-BLAUPAUSE

  • Unterstützungsniveau:

UNTERSTÜTZUNGS-BLAUPAUSE


Implementierung in MetaQuotes Language 5 (MQL5)

Nachdem wir alle Theorien über die Handelsstrategien mit Widerstand und Unterstützung gelernt haben, wollen wir die Theorie automatisieren und einen Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5) erstellen.

Um einen Expert Advisor (EA) zu erstellen, klicken Sie auf Ihrem MetaTrader 5-Terminal auf die Registerkarte Tools und aktivieren Sie MetaQuotes Language Editor oder drücken Sie einfach F4 auf Ihrer Tastatur. Alternativ können Sie auch auf das IDE-Symbol (Integrated Development Environment) in der Symbolleiste klicken. Dadurch wird die MetaQuotes-Spracheditor-Umgebung geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht.

META-EDITOR ÖFFNEN

Sobald der MetaEditor geöffnet ist, navigieren Sie in der Symbolleiste zur Registerkarte „Datei“ und wählen Sie „Neue Datei“, oder drücken Sie einfach die Tastenkombination STRG + N, um ein neues Dokument zu erstellen. Alternativ können Sie auch auf das Symbol New auf der Registerkarte Werkzeuge klicken. Daraufhin erscheint ein Popup-Fenster des MQL-Assistenten.

NEUER EA

Markieren Sie in dem sich öffnenden Assistenten die Option Expert Advisor (Vorlage bzw. template)) und klicken Sie auf Weiter (Next).

MQL WIZARD

Geben Sie in den allgemeinen Eigenschaften des Expertenberaters unter dem Abschnitt Name den Dateinamen Ihres Experten an. Beachten Sie, dass Sie den Backslash vor dem Namen des EA verwenden, um einen Ordner anzugeben oder zu erstellen, wenn er nicht existiert. Hier haben wir zum Beispiel standardmäßig „Experts\“. Das bedeutet, dass unser EA im Ordner Experts erstellt wird und wir ihn dort finden können. Die anderen Abschnitte sind ziemlich einfach, aber Sie können dem Link am Ende des Assistenten folgen, um zu erfahren, wie der Prozess genau abläuft.

EA-NAME

Nachdem Sie den gewünschten Expert Advisor-Dateinamen eingegeben haben, klicken Sie auf Weiter, dann auf Weiter und schließlich auf Fertig stellen. Nachdem wir all dies getan haben, können wir nun unsere Strategie programmieren.

Zunächst binden wir eine Handelsinstanz ein, indem wir #include am Anfang des Quellcodes verwenden. Dadurch erhalten wir Zugriff auf die Klasse CTrade, mit der wir ein Handelsobjekt erstellen werden. Dies ist von entscheidender Bedeutung, da wir sie zur Eröffnung von Geschäften benötigen.

#include <Trade/Trade.mqh>
CTrade obj_Trade;

Der Präprozessor wird die Zeile #include <Trade/Trade.mqh> durch den Inhalt der Datei Trade.mqh ersetzen. Die spitzen Klammern zeigen an, dass die Datei Trade.mqh aus dem Standardverzeichnis entnommen wird (normalerweise ist es das Terminal-Installationsverzeichnis\MQL5\Include). Das aktuelle Verzeichnis wird bei der Suche nicht berücksichtigt. Die Zeile kann an beliebiger Stelle im Programm platziert werden, aber in der Regel werden alle Einschlüsse am Anfang des Quellcodes platziert, um den Code besser zu strukturieren und die Referenz zu erleichtern. Die Deklaration des Objekts obj_Trade der Klasse CTrade ermöglicht uns dank der MQL5-Entwickler einen einfachen Zugriff auf die in dieser Klasse enthaltenen Methoden.

CTRADE CLASS

Im globalen Bereich müssen wir Arrays definieren, die unsere Höchst- und Tiefstpreisdaten enthalten, die wir später manipulieren und analysieren werden, um die Unterstützungs- und Widerstandsniveaus zu finden. Nachdem die Ebenen gefunden wurden, müssen wir sie ebenfalls in einem Array speichern, da es natürlich mehr als eine geben wird.

double pricesHighest[], pricesLowest[];

double resistanceLevels[2], supportLevels[2];

Hier deklarieren wir zwei „double“-Arrays, die die höchsten und niedrigsten Preise für eine festgelegte Datenmenge enthalten, und zwei weitere, die die identifizierten und sortierten Unterstützungs- und Widerstandsniveaus enthalten werden. Das sind in der Regel die beiden Koordinaten der jeweiligen Ebene. Beachten Sie, dass die Preisvariablen leer sind, was sie zu dynamischen Arrays ohne vordefinierte Größe macht, was bedeutet, dass sie eine beliebige Anzahl von Elementen basierend auf den bereitgestellten Daten enthalten können. Im Gegensatz dazu haben die Level-Variablen eine feste Größe von zwei, was sie zu statischen Arrays macht, d. h. sie können jeweils genau zwei Elemente enthalten. Wenn Sie mehr Koordinaten verwenden möchten, können Sie die Größe der Koordinaten auf die Anzahl der Punkte erhöhen, die Sie für angemessen halten.

Sobald wir die Niveaus identifiziert haben, müssen wir sie zu Visualisierungszwecken in das Chart einzeichnen. Daher müssen wir die Zeilennamen, die ihnen jeweils zugewiesenen Farben und die jeweiligen Präfixe zur leichteren Identifizierung und Eindeutigkeit festlegen, falls mehrere Experten auf demselben Handelskonto tätig sind. Dies ermöglicht es dem EA, mit anderen EAs kompatibel zu sein, da er seine Ebenen identifizieren und mit ihnen effektiv und unabhängig arbeiten kann.

#define resLine "RESISTANCE LEVEL"
#define colorRes clrRed
#define resline_prefix "R"

#define supLine "SUPPORT LEVEL"
#define colorSup clrBlue
#define supline_prefix "S"

Wir verwenden das Schlüsselwort #define, um ein Makro mit dem Namen „resLine“ und dem Wert „RESISTANCE LEVEL“ zu definieren, um den Namen des Widerstandsniveaus einfach zu speichern, anstatt den Namen bei jeder Erstellung des Niveaus erneut eingeben zu müssen, was uns viel Zeit spart und die Gefahr einer falschen Namensgebung verringert. Grundsätzlich werden Makros also zur Textersetzung während der Kompilierung verwendet.

In ähnlicher Weise legen wir die Farbe des Widerstandsniveaus als rot fest und definieren schließlich das Präfix „R“ für Widerstandsniveaus, das wir zur Kennzeichnung und Identifizierung von Widerstandslinien auf dem Chart verwenden werden. Ähnlich wie die Widerstandsniveaus definieren wir die Unterstützungsniveaus nach den gleichen Kriterien.

Sobald wir den EA initialisiert haben, müssen wir unsere Daten in eine Zeitreihe einordnen, also werden wir zuerst mit den neuesten Daten arbeiten und unsere Speicher-Arrays für die Aufnahme unserer Daten vorbereiten. Dies geschieht mit dem OnInit-Ereignis-Handler.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
//---
   
   ArraySetAsSeries(pricesHighest,true);
   ArraySetAsSeries(pricesLowest,true);
   // define the size of the arrays
   ArrayResize(pricesHighest,50);
   ArrayResize(pricesLowest,50);
//---
   return(INIT_SUCCEEDED);
}

Dabei treten zwei unterschiedliche Dinge auf. Zunächst setzen wir unsere Preisspeicher-Arrays als Zeitreihen mit der eingebauten MQL5-Funktion ArraySetAsSeries, die zwei Argumente benötigt, das Ziel-Array und das boolesche Flag, in diesem Fall true, um die Konvertierung zu akzeptieren. Das bedeutet, dass die Arrays so indiziert werden, dass die ältesten Daten den höchsten Index haben und die jüngsten Daten den Index 0. Hier ist ein Beispiel. Angenommen, wir rufen Daten von 2020 bis 2024 ab. Hier ist das Format, in dem wir die Daten erhalten.

Jahr Daten
2020 0
2021 1
2022 2
2023 3
2024 4

Daten im obigen Format sind nicht bequem zu verwenden, da sie in chronologischer Reihenfolge angeordnet sind, wobei die ältesten Daten am ersten Index indiziert sind, was bedeutet, dass sie als erste verwendet werden. Es ist zweckmäßiger, zuerst die neuesten Daten für die Analyse zu verwenden, und daher müssen wir die Daten in umgekehrter chronologischer Reihenfolge anordnen, um die nachstehenden Ergebnisse zu erzielen.

Jahr Daten
2024 4
2023 3
2022 2
2021 1
2020 0

Um das obige Format programmatisch zu erreichen, verwenden wir, wie bereits erläutert, die Funktion ArraySetAsSeries. Zweitens definieren wir die Größe der Arrays, indem wir die Funktion ArrayResize verwenden und angeben, dass sie jeweils fünfzig Elemente enthalten. Das kann alles Mögliche sein, nur ein willkürlicher Wert, den wir genommen haben, und Sie können ihn ignorieren. Aus formalen Gründen benötigen wir jedoch nicht zu viele Daten in unseren Arrays, da wir planen, die empfangenen Preisdaten so zu sortieren, dass nur die ersten zehn wichtigsten Daten vorhanden sind, und die zusätzliche Größe reserviert wird. Sie sehen also, warum es keinen Sinn macht, unsere Arrays größer zu machen.

In OnDeinit müssen wir die Speicherdaten, die verwendet wurden, aus dem Computerspeicher entfernen. Dies wird dazu beitragen, die Ressourcen zu schonen.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
//---
   ArrayFree(pricesHighest);
   ArrayFree(pricesLowest);
   
   //ArrayFree(resistanceLevels); // cannot be used for static allocated array
   //ArrayFree(supportLevels); // cannot be used for static allocated array
   
   ArrayRemove(resistanceLevels,0,WHOLE_ARRAY);
   ArrayRemove(supportLevels,0,WHOLE_ARRAY);
}

Wir verwenden die Funktion ArrayFree, um die Daten zu entfernen, die einen Teil des Computerspeichers belegen, da der EA nicht mehr verwendet wird und die Daten nutzlos sind. Die Funktion ist vom Typ void, der nur einen einzigen Parameter oder ein Argument, das dynamische Array, annimmt, seinen Puffer freigibt und die Größe der Null-Dimension auf 0 setzt. Für die statischen Arrays, in denen wir Unterstützungs- und Widerstandskurse speichern, kann die Funktion jedoch nicht verwendet werden. Das heißt aber nicht, dass wir die Daten nicht entfernen können. Wir rufen eine weitere Funktion ArrayRemove auf, um die Daten zu entfernen, die wir verwerfen wollen. Die Funktion ist vom Typ bool und benötigt drei Argumente, um eine bestimmte Anzahl von Elementen aus einem Array zu entfernen. Wir spezifizieren die Ziel-Array-Variable und geben den Index an, bei dem die Entfernungsoperation beginnt, in unserem Fall ist es Null, da wir alles entfernen wollen, und schließlich die Anzahl der zu entfernenden Elemente, in diesem Fall das gesamte Array, um alles loszuwerden.

Die meisten unserer Aktivitäten werden in OnTick ausgeführt. Dies wird eine reine Preisaktion sein und wir werden uns stark auf diesen Event-Handler verlassen. Werfen wir also einen Blick auf die Parameter, die die Funktion benötigt, denn sie sind das Herzstück dieses Codes.

void OnTick(){
//---

}

Wie wir bereits gesehen haben, handelt es sich um eine einfache, aber wichtige Funktion, die keine Argumente benötigt und nichts zurückgibt. Es handelt sich lediglich um eine void Funktion, d. h. sie gibt nichts zurück. Diese Funktion wird in Expert Advisors verwendet und wird ausgeführt, wenn es einen neuen Tick gibt, d. h. eine Änderung der Preisnotierungen für den jeweiligen Rohstoff.

Da wir nun gesehen haben, dass die Funktion OnTick bei jeder Änderung der Kursnotierungen generiert wird, müssen wir eine Kontrolllogik definieren, die es uns ermöglicht, den Code einmal pro Balken und nicht bei jedem Tick auszuführen, um zumindest unnötige Codeläufe zu vermeiden und somit den Gerätespeicher zu schonen. Dies ist bei der Suche nach Unterstützungs- und Widerstandsniveaus erforderlich. Wir brauchen nicht bei jedem Tick nach den Niveaus zu suchen und erhalten dennoch immer die gleichen Ergebnisse, vorausgesetzt, wir befinden uns immer noch auf derselben Kerze. Hier ist die Logik:

   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars) return;
   prevBars = currBars;

Zunächst deklarieren wir eine Integer-Variable „currBars“, die die berechnete Anzahl der aktuellen Balken auf dem Chart für das angegebene Handelssymbol und die Periode bzw. den Zeitrahmen speichert, wie Sie vielleicht schon gehört haben. Dies wird durch die Verwendung der Funktion iBars erreicht, die nur zwei Argumente benötigt, nämlich das Symbol und den Zeitrahmen. 

Dann deklarieren wir eine weitere statische Integer-Variable „prevBars“, um die Gesamtzahl der vorherigen Balken im Chart zu speichern, wenn ein neuer Balken erzeugt wird, und initialisieren sie mit dem Wert der aktuellen Balken im Chart für den ersten Durchlauf der Funktion. Wir werden ihn verwenden, um die aktuelle Anzahl der Balken mit der vorherigen Anzahl der Balken zu vergleichen, um den Zeitpunkt einer neuen Balkenerzeugung im Chart zu bestimmen.

Schließlich wird mit einer bedingten Anweisung geprüft, ob die aktuelle Anzahl der Kerzen gleich der vorherigen Anzahl der Kerzen ist. Wenn sie gleich sind, bedeutet dies, dass sich kein neuer Balken gebildet hat, sodass wir die weitere Ausführung beenden und zurückkehren. Andernfalls, wenn die Zählung des aktuellen und des vorherigen Balkens nicht gleich ist, bedeutet dies, dass sich ein neuer Balken gebildet hat. In diesem Fall aktualisieren wir die Variable „previousBars“ mit dem aktuellen Balken, sodass sie beim nächsten Tick gleich der Anzahl der Balken im Chart ist, es sei denn, wir gehen zu einem neuen Balken über.

Die für die Analyse zu berücksichtigenden Balken sind nur die sichtbaren Balken im Chart. Das liegt daran, dass wir die ältesten Daten für etwa zehn Millionen Balken nicht berücksichtigen müssen, da sie nutzlos wären. Stellen Sie sich einen Fall vor, in dem Sie eine Unterstützungsstufe haben, die weit ins Vorjahr zurückreicht. Das macht doch keinen Sinn, oder? Wir betrachten also nur die Balken, die auf dem Chart zu sehen sind, da dies die jüngsten brauchbaren Daten zu den aktuellen Marktbedingungen sind. Dazu verwenden wir die nachstehende Logik.

   int visible_bars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);

Wir deklarieren eine Integer-Variable visible_bars und verwenden die Funktion ChartGetInteger, um die Anzahl der sichtbaren Balken im Chart zu ermitteln. Da es sich um eine Funktion vom Typ long handelt, wird sie in eine int-Zahl umgewandelt, indem (int) vor der Funktion hinzugefügt wird. Wir könnten natürlich unsere Zielvariable als long definieren, aber wir brauchen nicht so viele Speicherbytes. 

Um die Ebenen zu finden, müssen wir eine Schleife über jeden Balken ziehen. Eine for-Schleife ist dafür unerlässlich.

   for (int i=1; i<=visible_bars-1; i++){
   ...
   
   }

Die Schleife wird ausgeführt, indem zunächst die ganzzahlige Variable i des Schleifenzählers auf 1 initialisiert wird, um den Startpunkt der Schleife zu bestimmen. Eine bedeutet, dass wir mit dem Balken vor dem aktuellen Balken beginnen, da sich der aktuelle Balken in der Entstehungsphase befindet und somit noch unentschieden ist. Sie kann zu allem führen. Es folgt die Bedingung, die erfüllt sein muss, damit die Schleife weiterläuft. Solange i kleiner oder gleich der Gesamtzahl der betrachteten Balken minus eins ist, wird die Schleife weiter ausgeführt. Schließlich wird der Schleifenzähler „i“ bei jedem Durchlauf der Schleife um eins erhöht. Einfach ausgedrückt: i++ ist dasselbe wie i=i+1. Wir könnten auch eine abwärts laufende Schleife verwenden, bei der der Schleifenzähler-Operator -- wäre, was zu einer Analyse führen würde, die mit dem ältesten letzten Balken bis zum aktuellen Balken beginnt, aber wir entscheiden uns für eine aufwärts laufende Schleife, sodass die Balkenanalyse mit dem jüngsten Balken bis zum ältesten Balken beginnt.

In jeder Schleife wählen wir einen Balken oder Kerze aus und müssen daher die Balken-Eigenschaften abrufen. In diesem Fall sind nur die Eigenschaften „open“, „high“, „low“, „close“ und „time“ des Balkens für uns wichtig.

      double open = iOpen(_Symbol,_Period,i);
      double high = iHigh(_Symbol,_Period,i);
      double low = iLow(_Symbol,_Period,i);
      double close = iClose(_Symbol,_Period,i);
      datetime time = iTime(_Symbol,_Period,i);

Hier deklarieren wir die jeweiligen Datentypvariablen und initialisieren sie mit den entsprechenden Daten. Verwenden Sie zum Beispiel die Funktion iOpen, um den Eröffnungskurs des Balkens zu ermitteln, indem Sie den Symbolnamen des Finanzinstruments, seinen Zeitrahmen und den Index des Zielbalkens angeben.

Nachdem wir die Daten des Balkens erhalten haben, müssen wir die Daten mit dem Rest der vorangegangenen Balken abgleichen, um einen Balken zu finden, der die gleichen Daten wie der ausgewählte Balken aufweist, was ein Niveau bedeutet, auf das der Preis in der Vergangenheit mehrfach reagiert hat. Es macht jedoch keinen Sinn, ein Unterstützungs- oder Widerstandsniveau zu haben, das zwei aufeinanderfolgende Niveaus umfasst. Die Ebenen sollten zumindest weit voneinander entfernt sein. Lassen Sie uns dies zunächst definieren.

      int diff_i_j = 10;

Wir definieren eine Integer-Variable, die die Differenz in Balken zwischen dem aktuellen Balken und dem Balken, der für die Prüfung einer Übereinstimmung auf demselben Niveau berücksichtigt werden soll, enthält. In unserem Fall sind es 10. Visuell dargestellt, ist das, was wir meinen.

BALKEN DIFFERENZ

Jetzt können wir eine Schleife einleiten, die die Logik enthält.

      for (int j=i+diff_i_j; j<=visible_bars-1; j++){
      ...
      
      }

Für die innere for-Schleife verwenden wir die ganzzahlige Zählervariable j, die bei zehn Balken ab dem aktuellen Balken beginnt und bis zum vorletzten Balken geht. Um dies zu verstehen und sich mit den Ergebnissen vertraut zu machen, bevor wir fortfahren, lassen Sie uns die Ausgabe ausdrucken, um sie zu visualisieren.

//Print in the outer loop
      Print(":: BAR NO: ",i);

      //Print in the inner loop
         Print("BAR CHECK NO: ",j);

AUSGEWÄHLTE SCHLEIFEN-BALKEN

Wie Sie sehen, wird die Schleife zum Beispiel bei einem ausgewählten Balken bei Index 15 10 Balken vor dem aktuell ausgewählten Balken initialisiert. Mathematisch gesehen ist das 15+10=25. Ab dem 25. Balken wird die Schleife dann bis zum vorletzten Balken ausgeführt, der in unserem Fall 33 ist.

Nun, da wir das Intervall der Balken korrekt auswählen können, können wir auch die Eigenschaften der ausgewählten Balken abrufen.

         double open_j = iOpen(_Symbol,_Period,j);
         double high_j = iHigh(_Symbol,_Period,j);
         double low_j = iLow(_Symbol,_Period,j);
         double close_j = iClose(_Symbol,_Period,j);
         datetime time_j = iTime(_Symbol,_Period,j);

Es gilt die gleiche Logik wie bei der Abfrage von Eigenschaften in der äußeren Schleife. Der einzige Unterschied besteht darin, dass wir unsere Variablen mit einem zusätzlichen Unterstrich j definieren, um zu verdeutlichen, dass die Eigenschaften für die innere for-Schleife gelten und der Zielindex des Balkens sich zu j ändert.

Da wir nun alle erforderlichen Kursdaten haben, können wir die Unterstützungs- und Widerstandsniveaus ermitteln.

         // CHECK FOR RESISTANCE
         double high_diff = NormalizeDouble((MathAbs(high-high_j)/_Point),0);
         bool is_resistance = high_diff <= 10;
         
         // CHECK FOR SUPPORT
         double low_diff = NormalizeDouble((MathAbs(low-low_j)/_Point),0);
         bool is_support = low_diff <= 10;

Um die Widerstandsniveaus zu überprüfen, definieren wir eine double-Variable high_diff, die unsere Daten für die Differenz zwischen dem Hoch des aktuell ausgewählten Balkens in der äußeren Schleife und dem aktuell ausgewählten Balken in der inneren Schleife speichert. Die Funktion MathAbs wird verwendet, um sicherzustellen, dass das Ergebnis eine positive Zahl ist, unabhängig davon, welcher Preis höher ist, indem der Absolut- oder Moduluswert der Eingabe zurückgegeben wird. Zum Beispiel könnten wir 0,65432 - 0,05456 = -0,00024 haben. Unsere Antwort enthält einen negativen Wert, aber die Funktion ignoriert das negative Vorzeichen und gibt 0,00024 aus. Auch hier teilen Sie das Ergebnis durch den Punktwert, die kleinstmögliche Preisänderung eines Instruments, um die Differenz als Punktanzahl zu erhalten. In unserem Beispiel wäre dies 0,00024/0,00001 = 24,0. Schließlich, um genau zu sein, formatieren wir die Fließkommazahl mit der Funktion NormalizeDouble auf eine bestimmte Anzahl von Ziffern. In diesem Fall haben wir Null, was bedeutet, dass unsere Ausgabe eine ganze Zahl sein wird. In unserem Beispiel hätten wir also 24 ohne Dezimalpunkt.

Dann prüfen wir, ob die Differenz kleiner oder gleich zehn ist, ein vordefinierter Bereich, in dem die Differenz zulässig ist, und speichern das Ergebnis in einer booleschen Variablen is_resistance. Die gleiche Logik gilt für die Überprüfung der Förderstufe.

Wenn die Bedingungen unserer Niveaus erfüllt sind, werden unsere Unterstützungs- und Widerstandsniveaus wahr sein, andernfalls werden sie falsch sein. Um zu sehen, ob wir die Ebenen identifizieren können, drucken wir sie in das Journal. Um alles zu überprüfen, drucken wir die Preise der Koordinaten der Widerstandsniveaus zusammen mit ihren Preisunterschieden aus, um sicherzustellen, dass sie unsere Bedingungen erfüllen.

         if (is_resistance){
            Print("RESISTANCE AT BAR ",i," (",high,") & ",j," (",high_j,"), Pts = ",high_diff);
          ...  
         }

Hier ist das Ergebnis, das wir erhalten.

AUSDRUCK der WIDERSTÄNDE

Wir können die Ebenen identifizieren, aber es sind nur irgendwelche Ebenen. Da wir Pegel an den höchsten oder niedrigsten Punkten haben wollen, brauchen wir eine zusätzliche Kontrolllogik, um sicherzustellen, dass wir nur die wichtigsten Pegel berücksichtigen. Dazu müssen wir die Höchst- und Tiefstpreise der betrachteten Balken kopieren, sie in aufsteigender bzw. absteigender Reihenfolge sortieren und dann den ersten bzw. letzten Betrag der benötigten Balken nehmen. Wir tun dies vor den for-Schleifen.

   ArrayFree(pricesHighest);
   ArrayFree(pricesLowest);
   
   int copiedBarsHighs = CopyHigh(_Symbol,_Period,1,visible_bars,pricesHighest);
   int copiedBarsLows = CopyLow(_Symbol,_Period,1,visible_bars,pricesLowest);

Vor der Speicherung befreien wir unsere Arrays von allen Daten. Anschließend kopieren wir die Höchstwerte der Balken in das Zielfeld. Dies wird durch die Verwendung der Funktion CopyHigh vom Datentyp Integer erreicht, indem das Symbol, die Periode, der Startindex des zu kopierenden Balkens, die Anzahl der Balken und das Zielspeicherarray angegeben werden. Das Ergebnis, d. h. die Anzahl der kopierten Balken, wird der Integer-Variablen copiedBarsHighs zugewiesen. Das Gleiche gilt für die Preise der Tiefs. Um sicherzustellen, dass wir die Daten erhalten, drucken wir die Arrays mit der Funktion ArrayPrint in das Journal.

   ArrayPrint(pricesHighest);
   ArrayPrint(pricesLowest);

Das sind die Ergebnisse, die wir erhalten.

UNSORTIERTE DATEN

Anschließend sortieren wir die Daten mit der Funktion ArraySort in aufsteigender Reihenfolge und drucken die Ergebnisse erneut aus.

         // sort the array in ascending order
   ArraySort(pricesHighest);
   ArraySort(pricesLowest);

   ArrayPrint(pricesHighest);
   ArrayPrint(pricesLowest);

Das ist das Ergebnis.

SORTIERTE DATEN IN AUFSTEIGENDER REIHENFOLGE

Schließlich müssen wir die ersten zehn Preise, d. h. die Hochs, und die letzten zehn Preise, d. h. die Tiefs, ermitteln, die unsere Extrempunkte bilden werden.

   ArrayRemove(pricesHighest,10,WHOLE_ARRAY);

Um die ersten zehn Hochs der Balken mit den Hochs zu erhalten, verwenden wir die Funktion ArrayRemove und geben das Ziel-Array, den Index, bei dem die Entfernung beginnt, in unserem Fall zehn, und schließlich die Anzahl der zu entfernenden Elemente, in unserem Fall den Rest der Daten, an.

Um die letzten zehn Tiefs der Balken mit den Tiefs zu erhalten, wird ein ähnlicher Vorgang durchgeführt, allerdings mit einer komplexeren und weniger einfachen Methode.

   ArrayRemove(pricesLowest,0,visible_bars-10);

Wir verwenden die gleiche Funktion, aber unser Startindex ist Null, da wir nicht an den ersten Werten interessiert sind, und die Anzahl ist die Gesamtzahl der betrachteten Balken minus zehn. Wenn wir die Daten drucken, erhalten wir die folgende Ausgabe.

ENDGÜLTIGE FESTE BENÖTIGTE DATEN

Da wir nun die Daten für die höchsten Balken haben, können wir weiterhin die Niveaus überprüfen und die gültigen Setups bestimmen. Wir starten eine weitere for-Schleife, um die Operation durchzuführen.

            for (int k=0; k<ArraySize(pricesHighest); k++){
            ...
            }

Diesmal beginnt unsere Zählervariable k bei Null, da wir alle Preise im Array berücksichtigen wollen. 

Da wir die Preisübereinstimmungen finden wollen, deklarieren wir boolesche Speichervariablen, die die Flags der Übereinstimmungsergebnisse außerhalb der for-Schleifen speichern werden, und initialisieren sie auf false.

   bool matchFound_high1 = false, matchFound_low1 = false;
   bool matchFound_high2 = false, matchFound_low2 = false;

Wenn der ausgewählte gespeicherte Preis gleich dem Hoch des Balkens in der ersten Schleife ist, setzen wir das Flag für das erste gefundene Hoch auf true und informieren die Instanz. Ähnlich verhält es sich, wenn der gewählte gespeicherte Preis gleich dem Höchststand des Balkens in der zweiten Schleife ist, setzen wir das Flag für den zweiten gefundenen Höchststand auf true und informieren die Instanz.

               if (pricesHighest[k]==high){
                  matchFound_high1 = true;
                  Print("> RES H1(",high,") FOUND @ ",k," (",pricesHighest[k],")");
               }
               if (pricesHighest[k]==high_j){
                  matchFound_high2 = true;
                  Print("> RES H2(",high_j,") FOUND @ ",k," (",pricesHighest[k],")");
               }

Wenn die beiden Koordinaten übereinstimmen, aber die aktuellen Widerstandsniveaus mit den Preisen übereinstimmen, bedeutet dies, dass wir die Niveaus bereits haben. Wir brauchen also keine weiteren Widerstandsniveaus zu schaffen. Wir informieren die Instanz, setzen das Flag stop_processing auf true und brechen die Schleife vorzeitig ab.

               if (matchFound_high1 && matchFound_high2){
                  if (resistanceLevels[0]==high || resistanceLevels[1]==high_j){
                     Print("CONFIRMED BUT This is the same resistance level, skip updating!");
                     stop_processing = true; // Set the flag to stop processing
                     break; // stop the inner loop prematurely
                  }
                  ...
                  
               }

Das Flag stop_processing wird außerhalb der for-Schleifen im oberen Teil definiert und in die erste for-Schleifen-Ausführungslogik integriert, um sicherzustellen, dass wir Ressourcen sparen.

   bool stop_processing = false; // Flag to control outer loop

//...
   for (int i=1; i<=visible_bars-1 && !stop_processing; i++){
      ...
   
   }

Wenn die beiden Koordinaten übereinstimmen, aber nicht mit den aktuellen Kursen übereinstimmen, bedeutet dies, dass wir ein weiteres neues Widerstandsniveau haben und es auf die neuesten Daten aktualisieren können. 

                  else {
                     Print(" ++++++++++ RESISTANCE LEVELS CONFIRMED @ BARS ",i,
                     "(",high,") & ",j,"(",high_j,")");
                     resistanceLevels[0] = high;
                     resistanceLevels[1] = high_j;
                     ArrayPrint(resistanceLevels);
                     
                     ...
                  }

Hier ist die Visualisierung der Ergebnisse, die wir erhalten.

BESTÄTIGTE WIDERSTANDSNIVEAUS

Um die Niveaus zu visualisieren, lassen Sie uns diese im Chart abbilden.

                     draw_S_R_Level(resLine,high,colorRes,5);
                     draw_S_R_Level_Point(resline_prefix,high,time,218,-1,colorRes,90);
                     draw_S_R_Level_Point(resline_prefix,high,time_j,218,-1,colorRes,90);

                     stop_processing = true; // Set the flag to stop processing
                     break;

Wir verwenden zwei Funktionen für diese Aufgabe. Die erste Funktion draw_S_R_Level nimmt den Namen der zu zeichnenden Linie, den Preis, die Farbe und die Breite der Linie.

void draw_S_R_Level(string levelName,double price,color clr,int width){
   if (ObjectFind(0,levelName) < 0){
      ObjectCreate(0,levelName,OBJ_HLINE,0,TimeCurrent(),price);
      ObjectSetInteger(0,levelName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,levelName,OBJPROP_WIDTH,width);
   }
   else {
      ObjectSetDouble(0,levelName,OBJPROP_PRICE,price);
   }
   ChartRedraw(0);
}

Die Funktion ist vom Typ void, das heißt, sie gibt nichts zurück. Anschließend wird mit einer bedingten Anweisung geprüft, ob das Objekt existiert, indem die Funktion ObjectFind verwendet wird, die eine negative Ganzzahl zurückgibt, falls das Objekt nicht gefunden wird. Ist dies der Fall, wird das Objekt mit der Bezeichnung OBJ_HLINE zum aktuellen Zeitpunkt und zum angegebenen Preis erstellt, da es nur eine einzige Koordinate benötigt. Dann legen wir seine Farbe und Breite fest. Wenn das Objekt gefunden wird, aktualisieren wir einfach seinen Preis auf den angegebenen Preis und zeichnen das Chart neu, damit die aktuellen Änderungen übernommen werden. Diese Funktion zeichnet nur eine einfache Linie in das Chart. Das ist das Ergebnis.

GLATTE WIDERSTANDSLINIE

Die zweite Funktion draw_S_R_Level_Point nimmt den Namen der zu zeichnenden Linie, den Preis, die Zeit, den Pfeilcode, die Richtung, die Farbe und den Winkel der Beschriftung auf. Diese Funktion zeichnet die Niveaupunkte so, dass sie auf der eingezeichneten Widerstandslinie besser definiert sind.
void draw_S_R_Level_Point(string objName,double price,datetime time,
      int arrowcode,int direction,color clr,double angle){
   //objName = " ";
   StringConcatenate(objName,objName," @ \nTime: ",time,"\nPrice: ",DoubleToString(price,_Digits));
   if (ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) {
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrowcode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
   }
   string prefix = resline_prefix;
   string txt = "\n"+prefix+"("+DoubleToString(price,_Digits)+")";
   string objNameDescription = objName + txt;
   if (ObjectCreate(0,objNameDescription,OBJ_TEXT,0,time,price)) {
     // ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "" + txt);
      ObjectSetInteger(0,objNameDescription,OBJPROP_COLOR,clr);
      ObjectSetDouble(0,objNameDescription,OBJPROP_ANGLE, angle);
      ObjectSetInteger(0,objNameDescription,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_LEFT);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
   }
   ChartRedraw(0);
}

Die nutzerdefinierte Funktion „draw_S_R_Level_Point“ benötigt sieben Parameter, um ihre Wiederverwendbarkeit zu erleichtern. Die Funktionen der Parameter sind wie folgt:

  • objName: Eine Zeichenfolge, die den Namen des zu erstellenden grafischen Objekts angibt.
  • price: Ein double-Wert, der die Preiskoordinate angibt, an der das Objekt platziert werden soll.
  • time: Ein datetime-Wert, der die Zeitkoordinate angibt, an der das Objekt platziert werden soll.
  • arrowCode: Eine ganze Zahl, die den Pfeilcode für das Pfeilobjekt angibt.
  • direction: Eine ganze Zahl, die die Richtung (nach oben oder unten) für die Positionierung der Textbeschriftung angibt.
  • clr: Ein Farbwert (z. B. clrBlue, clrRed) für die grafischen Objekte.
  • angle: Ausrichtungswinkel der Beschriftung.

Die Funktion verkettet zunächst den Namen des Objekts mit Zeit und Preis zur Unterscheidung der Levelpunkte. Dadurch wird sichergestellt, dass beim Überfahren des Etiketts mit dem Mauszeiger die Beschreibung mit der spezifischen Zeit und dem Preis der Koordinate eingeblendet wird.

Die Funktion prüft dann, ob im Chart bereits ein Objekt mit dem angegebenen objName existiert. Wenn nicht, werden die Objekte erstellt. Die Erstellung des Objekts erfolgt mit Hilfe der integrierten Funktion „ObjectCreate“, die die Angabe des zu zeichnenden Objekts, in diesem Fall des Pfeilobjekts mit der Bezeichnung „OBJ_ARROW“, sowie der Zeit und des Preises erfordert, die die Ordinaten des Objekterstellungspunkts bilden. Anschließend legen wir die Objekteigenschaften Pfeilcode, Farbe, Schriftgröße und Ankerpunkt fest. Für den Pfeilcode verfügt MQL5 über einige bereits vordefinierte Zeichen der Schriftart Wingdings, die direkt verwendet werden können. Hier ist die Tabelle, in der die Zeichen angegeben sind:

WINGDINGS

Bis zu diesem Punkt zeichnen wir den angegebenen Pfeil nur wie folgt in das Chart ein:

WIDERSTAND + PFEIL

Wir können sehen, dass es uns gelungen ist, die Widerstandspunkte mit dem angegebenen Pfeilcode zu zeichnen, in diesem Fall haben wir den Pfeilcode 218 verwendet, aber es gibt keine Beschreibung von ihnen. Um die entsprechende Beschreibung hinzuzufügen, verketten wir den Pfeil mit einem Text. Wir erstellen ein weiteres Textobjekt mit der Bezeichnung „OBJ_TEXT“ und legen auch die entsprechenden Eigenschaften fest. Die Textbeschriftung dient als beschreibende Anmerkung zu den Widerstandspunkten, indem sie zusätzlichen Kontext oder Informationen zu den Widerstandspunkten liefert und sie für Händler und Analysten informativer macht. Wir wählen den Wert des Textes als einen bestimmten Preis, der bedeutet, dass es sich um einen Widerstandspunkt handelt.

Die Variable „objNameDescription“ wird dann durch Verkettung des ursprünglichen „objName“ mit dem beschreibenden Text erstellt. Dieser kombinierte Name stellt sicher, dass der Pfeil und die zugehörige Textbeschriftung miteinander verknüpft sind. Dieses spezielle Codeschnipsel wird verwendet, um dies zu erreichen.

   string prefix = resline_prefix;
   string txt = "\n"+prefix+"("+DoubleToString(price,_Digits)+")";
   string objNameDescription = objName + txt;
   if (ObjectCreate(0,objNameDescription,OBJ_TEXT,0,time,price)) {
     // ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "" + txt);
      ObjectSetInteger(0,objNameDescription,OBJPROP_COLOR,clr);
      ObjectSetDouble(0,objNameDescription,OBJPROP_ANGLE, angle);
      ObjectSetInteger(0,objNameDescription,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_LEFT);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
   }

Dies ist das Ergebnis der Verkettung der Widerstandspunkte mit ihren Beschreibungen.

WIDERSTAND + PFEIL + BESCHREIBUNG

Um die Unterstützungsniveaus abzubilden, gilt die gleiche Logik, jedoch mit umgekehrten Bedingungen.

         else if (is_support){
            //Print("SUPPORT AT BAR ",i," (",low,") & ",j," (",low_j,"), Pts = ",low_diff);
            
            for (int k=0; k<ArraySize(pricesLowest); k++){
               if (pricesLowest[k]==low){
                  matchFound_low1 = true;
                  //Print("> SUP L1(",low,") FOUND @ ",k," (",pricesLowest[k],")");
               }
               if (pricesLowest[k]==low_j){
                  matchFound_low2 = true;
                  //Print("> SUP L2(",low_j,") FOUND @ ",k," (",pricesLowest[k],")");
               }
               if (matchFound_low1 && matchFound_low2){
                  if (supportLevels[0]==low || supportLevels[1]==low_j){
                     Print("CONFIRMED BUT This is the same support level, skip updating!");
                     stop_processing = true; // Set the flag to stop processing
                     break; // stop the inner loop prematurely
                  }
                  else {
                     Print(" ++++++++++ SUPPORT LEVELS CONFIRMED @ BARS ",i,
                     "(",low,") & ",j,"(",low_j,")");
                     supportLevels[0] = low;
                     supportLevels[1] = low_j;
                     ArrayPrint(supportLevels);
                     
                     draw_S_R_Level(supLine,low,colorSup,5);
                     draw_S_R_Level_Point(supline_prefix,low,time,217,1,colorSup,-90);
                     draw_S_R_Level_Point(supline_prefix,low,time_j,217,1,colorSup,-90);

                     stop_processing = true; // Set the flag to stop processing
                     break;
                  }
               }
            }
         }

Das Endergebnis, das wir aufgrund der Identifizierung der Ebenen und ihrer jeweiligen Zuordnung zum Chart-Meilenstein erhalten, sieht wie folgt aus.

UNTERSTÜTZUNGS- UND WIDERSTANDSNIVEAUS

Wir überwachen dann die Niveaus und wenn die Niveaus außerhalb der sichtbaren Nähe der Balken liegen, betrachten wir das Widerstandsniveau als ungültig und löschen es. Um dies zu erreichen, müssen wir die Zeilen auf der Objektebene finden, und wenn wir sie gefunden haben, überprüfen wir die Bedingungen auf ihre Gültigkeit.

   if (ObjectFind(0,resLine) >= 0){
      double objPrice = ObjectGetDouble(0,resLine,OBJPROP_PRICE);
      double visibleHighs[];
      ArraySetAsSeries(visibleHighs,true);
      CopyHigh(_Symbol,_Period,1,visible_bars,visibleHighs);
      //Print("Object Found & visible bars is: ",ArraySize(visibleHighs));
      //ArrayPrint(visibleHighs);
      bool matchHighFound = false;

      ...
   }

Hier prüfen wir, ob das Objekt der Widerstandslinie gefunden wurde, und wenn ja, erhalten wir seinen Preis. Wir kopieren wieder die Höchstwerte der sichtbaren Balken im Chart und speichern sie in der Double-Array-Variablen visibleHighs.

Danach gehen wir in einer Schleife über die Höchstpreise und versuchen herauszufinden, ob es eine Übereinstimmung zwischen dem Preis des aktuell ausgewählten Balkens und dem Preis der Widerstandslinie gibt. Wenn es eine Übereinstimmung gibt, setzen wir das Flag matchHighFound auf true und beenden die Schleife.

      for (int i=0; i<ArraySize(visibleHighs); i++){
         if (visibleHighs[i] == objPrice){
            Print("> Match price for resistance found at bar # ",i+1," (",objPrice,")");
            matchHighFound = true;
            break;
         }
      }

Wenn es keine Übereinstimmung gibt, bedeutet dies, dass die Widerstandsstufe nicht in der Nähe ist. Wir informieren über die Instanz und verwenden eine nutzerdefinierte Funktion zum Löschen des Objekts.

      if (!matchHighFound){
         Print("(",objPrice,") > Match price for the resistance line not found. Delete!");
         deleteLevel(resLine);
      }

Die nutzerdefinierte Funktion deleteLevel benötigt nur ein Argument, den Namen der zu löschenden Ebene, und verwendet die Funktion ObjectDelete, um das definierte Objekt zu löschen.

void deleteLevel(string levelName){
   ObjectDelete(0,levelName);
   ChartRedraw(0);
}

Die gleiche Logik gilt für die Unterstützungslinie, aber es herrschen umgekehrte Bedingungen.

   if (ObjectFind(0,supLine) >= 0){
      double objPrice = ObjectGetDouble(0,supLine,OBJPROP_PRICE);
      double visibleLows[];
      ArraySetAsSeries(visibleLows,true);
      CopyLow(_Symbol,_Period,1,visible_bars,visibleLows);
      //Print("Object Found & visible bars is: ",ArraySize(visibleLows));
      //ArrayPrint(visibleLows);
      bool matchLowFound = false;
      
      for (int i=0; i<ArraySize(visibleLows); i++){
         if (visibleLows[i] == objPrice){
            Print("> Match price for support found at bar # ",i+1," (",objPrice,")");
            matchLowFound = true;
            break;
         }
      }
      if (!matchLowFound){
         Print("(",objPrice,") > Match price for the support line not found. Delete!");
         deleteLevel(supLine);
      }
   }

Schließlich, wenn bis zu diesem Punkt, der Widerstand und die Unterstützung Ebenen sind immer noch innerhalb des Charts, bedeutet dies, dass sie gültig sind, und so können wir weiterhin eine Logik, die bestimmen, ob sie gebrochen werden und offenen Markt Position zu schaffen. Betrachten wir zunächst den Durchbruch des Widerstandsniveaus.

Da der Preis mehrmals über das Widerstandsniveau ausbrechen könnte, was zu mehreren Signalgenerierungen führt, benötigen wir eine Logik, die sicherstellt, dass wir, sobald wir ein Widerstandsniveau durchbrechen und ein Signal generieren, das Signal nicht erneut auslösen, wenn wir es danach durchbrechen, wenn es sich um dasselbe Niveau handelt. Um dies zu erreichen, deklarieren wir eine statische double-Variable, die unseren Preis für das Signal enthält, der seinen Wert beibehält, bis wir ein anderes Signal erhalten.

   static double ResistancePriceTrade = 0;
   if (ObjectFind(0,resLine) >= 0){
      double ResistancePriceLevel = ObjectGetDouble(0,resLine,OBJPROP_PRICE);
      if (ResistancePriceTrade != ResistancePriceLevel){
      ...

   }

Wir prüfen dann, ob die Widerstandslinie existiert, und wenn sie existiert, erhalten wir ihren Preis. Mithilfe einer bedingten Anweisung prüfen wir, ob das Signal nicht gleich dem Preis der Widerstandslinie ist, was bedeutet, dass wir noch kein Signal für dieses bestimmte Niveau generiert haben und dass wir mit der Prüfung des Ausbruchssignals fortfahren können. Für den Check-up benötigen wir die Daten des vorangegangenen Balkens sowie aktualisierte Kurse.

         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
         double open1 = iOpen(_Symbol,_Period,1);
         double high1 = iHigh(_Symbol,_Period,1);
         double low1 = iLow(_Symbol,_Period,1);
         double close1 = iClose(_Symbol,_Period,1);

Anschließend prüfen wir mit bedingten Anweisungen, ob der Kurs die Widerstandsmarke gekreuzt hat. Ist dies der Fall, informieren wir über das Verkaufssignal durch einen Ausdruck im Journal. Dann verwenden wir unser Handelsobjekt und den Punkt-Operator, um Zugriff auf die Sell Entry-Methode zu erhalten und die erforderlichen Parameter anzugeben. Schließlich aktualisieren wir den Wert der Signalvariablen auf den aktuellen Widerstandswert, damit wir kein weiteres Signal auf der Grundlage desselben Widerstandswertes erzeugen müssen.

         if (open1 > close1 && open1 < ResistancePriceLevel
            && high1 > ResistancePriceLevel && Bid < ResistancePriceLevel){
            Print("$$$$$$$$$$$$ SELL NOW SIGNAL!");
            obj_Trade.Sell(0.01,_Symbol,Bid,Bid+350*5*_Point,Bid-350*_Point);
            ResistancePriceTrade = ResistancePriceLevel;
         }

Die gleiche Logik gilt für die Logik des Unterstützungsausbruchs, aber es herrschen umgekehrte Bedingungen.

   static double SupportPriceTrade = 0;
   if (ObjectFind(0,supLine) >= 0){
      double SupportPriceLevel = ObjectGetDouble(0,supLine,OBJPROP_PRICE);
      if (SupportPriceTrade != SupportPriceLevel){
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
         double open1 = iOpen(_Symbol,_Period,1);
         double high1 = iHigh(_Symbol,_Period,1);
         double low1 = iLow(_Symbol,_Period,1);
         double close1 = iClose(_Symbol,_Period,1);

         if (open1 < close1 && open1 > SupportPriceLevel
            && low1 < SupportPriceLevel && Ask > SupportPriceLevel){
            Print("$$$$$$$$$$$$ BUY NOW SIGNAL!");
            obj_Trade.Buy(0.01,_Symbol,Ask,Ask-350*5*_Point,Ask+350*_Point);
            SupportPriceTrade = SupportPriceLevel;
         }
         
      }
   }

Hier ist die Darstellung des Meilensteins.

VOLLSTÄNDIGE UNTERSTÜTZUNGS- UND WIDERSTANDSTRANSAKTIONEN

Im Folgenden finden Sie den vollständigen Code, der benötigt wird, um einen eine Unterstützungs- und Widerstandshandelsstrategie in MQL5 zu erstellen, die die Levels identifiziert, sie im Chart abbildet und entsprechend Marktpositionen eröffnet.

//+------------------------------------------------------------------+
//|                                       RESISTANCE AND SUPPORT.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

//bool stop_processing = false;

double pricesHighest[], pricesLowest[];

double resistanceLevels[2], supportLevels[2];


#define resLine "RESISTANCE LEVEL"
#define colorRes clrRed
#define resline_prefix "R"

#define supLine "SUPPORT LEVEL"
#define colorSup clrBlue
#define supline_prefix "S"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
//---
   
   ArraySetAsSeries(pricesHighest,true);
   ArraySetAsSeries(pricesLowest,true);
   // define the size of the arrays
   ArrayResize(pricesHighest,50);
   ArrayResize(pricesLowest,50);
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
//---
   ArrayFree(pricesHighest);
   ArrayFree(pricesLowest);
   
   //ArrayFree(resistanceLevels); // cannot be used for static allocated array
   //ArrayFree(supportLevels); // cannot be used for static allocated array
   
   ArrayRemove(resistanceLevels,0,WHOLE_ARRAY);
   ArrayRemove(supportLevels,0,WHOLE_ARRAY);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
//---
   
   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if (prevBars == currBars) return;
   prevBars = currBars;
   
   int visible_bars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   bool stop_processing = false; // Flag to control outer loop
   bool matchFound_high1 = false, matchFound_low1 = false;
   bool matchFound_high2 = false, matchFound_low2 = false;
   
   ArrayFree(pricesHighest);
   ArrayFree(pricesLowest);
   
   int copiedBarsHighs = CopyHigh(_Symbol,_Period,1,visible_bars,pricesHighest);
   int copiedBarsLows = CopyLow(_Symbol,_Period,1,visible_bars,pricesLowest);
   
   //ArrayPrint(pricesHighest);
   //ArrayPrint(pricesLowest);
         // sort the array in ascending order
   ArraySort(pricesHighest);
   ArraySort(pricesLowest);
   //ArrayPrint(pricesHighest);
   //ArrayPrint(pricesLowest);
   ArrayRemove(pricesHighest,10,WHOLE_ARRAY);
   ArrayRemove(pricesLowest,0,visible_bars-10);
   //Print("FIRST 10 HIGHEST PRICES:");
   //ArrayPrint(pricesHighest);
   //Print("LAST 10 LOWEST PRICES:");
   //ArrayPrint(pricesLowest);
   
   for (int i=1; i<=visible_bars-1 && !stop_processing; i++){
      //Print(":: BAR NO: ",i);
      double open = iOpen(_Symbol,_Period,i);
      double high = iHigh(_Symbol,_Period,i);
      double low = iLow(_Symbol,_Period,i);
      double close = iClose(_Symbol,_Period,i);
      datetime time = iTime(_Symbol,_Period,i);
      
      int diff_i_j = 10;
      
      for (int j=i+diff_i_j; j<=visible_bars-1; j++){
         //Print("BAR CHECK NO: ",j);
         double open_j = iOpen(_Symbol,_Period,j);
         double high_j = iHigh(_Symbol,_Period,j);
         double low_j = iLow(_Symbol,_Period,j);
         double close_j = iClose(_Symbol,_Period,j);
         datetime time_j = iTime(_Symbol,_Period,j);
         
         // CHECK FOR RESISTANCE
         double high_diff = NormalizeDouble((MathAbs(high-high_j)/_Point),0);
         bool is_resistance = high_diff <= 10;
         
         // CHECK FOR SUPPORT
         double low_diff = NormalizeDouble((MathAbs(low-low_j)/_Point),0);
         bool is_support = low_diff <= 10;
         
         if (is_resistance){
            //Print("RESISTANCE AT BAR ",i," (",high,") & ",j," (",high_j,"), Pts = ",high_diff);
            
            for (int k=0; k<ArraySize(pricesHighest); k++){
               if (pricesHighest[k]==high){
                  matchFound_high1 = true;
                  //Print("> RES H1(",high,") FOUND @ ",k," (",pricesHighest[k],")");
               }
               if (pricesHighest[k]==high_j){
                  matchFound_high2 = true;
                  //Print("> RES H2(",high_j,") FOUND @ ",k," (",pricesHighest[k],")");
               }
               if (matchFound_high1 && matchFound_high2){
                  if (resistanceLevels[0]==high || resistanceLevels[1]==high_j){
                     Print("CONFIRMED BUT This is the same resistance level, skip updating!");
                     stop_processing = true; // Set the flag to stop processing
                     break; // stop the inner loop prematurily
                  }
                  else {
                     Print(" ++++++++++ RESISTANCE LEVELS CONFIRMED @ BARS ",i,
                     "(",high,") & ",j,"(",high_j,")");
                     resistanceLevels[0] = high;
                     resistanceLevels[1] = high_j;
                     ArrayPrint(resistanceLevels);
                     
                     draw_S_R_Level(resLine,high,colorRes,5);
                     draw_S_R_Level_Point(resline_prefix,high,time,218,-1,colorRes,90);
                     draw_S_R_Level_Point(resline_prefix,high,time_j,218,-1,colorRes,90);

                     stop_processing = true; // Set the flag to stop processing
                     break;
                  }
               }
            }
         }
         
         else if (is_support){
            //Print("SUPPORT AT BAR ",i," (",low,") & ",j," (",low_j,"), Pts = ",low_diff);
            
            for (int k=0; k<ArraySize(pricesLowest); k++){
               if (pricesLowest[k]==low){
                  matchFound_low1 = true;
                  //Print("> SUP L1(",low,") FOUND @ ",k," (",pricesLowest[k],")");
               }
               if (pricesLowest[k]==low_j){
                  matchFound_low2 = true;
                  //Print("> SUP L2(",low_j,") FOUND @ ",k," (",pricesLowest[k],")");
               }
               if (matchFound_low1 && matchFound_low2){
                  if (supportLevels[0]==low || supportLevels[1]==low_j){
                     Print("CONFIRMED BUT This is the same support level, skip updating!");
                     stop_processing = true; // Set the flag to stop processing
                     break; // stop the inner loop prematurely
                  }
                  else {
                     Print(" ++++++++++ SUPPORT LEVELS CONFIRMED @ BARS ",i,
                     "(",low,") & ",j,"(",low_j,")");
                     supportLevels[0] = low;
                     supportLevels[1] = low_j;
                     ArrayPrint(supportLevels);
                     
                     draw_S_R_Level(supLine,low,colorSup,5);
                     draw_S_R_Level_Point(supline_prefix,low,time,217,1,colorSup,-90);
                     draw_S_R_Level_Point(supline_prefix,low,time_j,217,1,colorSup,-90);

                     stop_processing = true; // Set the flag to stop processing
                     break;
                  }
               }
            }
         }
         
         
         
         if (stop_processing){break;}
      }
      if (stop_processing){break;}
   }
   
   if (ObjectFind(0,resLine) >= 0){
      double objPrice = ObjectGetDouble(0,resLine,OBJPROP_PRICE);
      double visibleHighs[];
      ArraySetAsSeries(visibleHighs,true);
      CopyHigh(_Symbol,_Period,1,visible_bars,visibleHighs);
      //Print("Object Found & visible bars is: ",ArraySize(visibleHighs));
      //ArrayPrint(visibleHighs);
      bool matchHighFound = false;
      
      for (int i=0; i<ArraySize(visibleHighs); i++){
         if (visibleHighs[i] == objPrice){
            Print("> Match price for resistance found at bar # ",i+1," (",objPrice,")");
            matchHighFound = true;
            break;
         }
      }
      if (!matchHighFound){
         Print("(",objPrice,") > Match price for the resistance line not found. Delete!");
         deleteLevel(resLine);
      }
   }
   
   if (ObjectFind(0,supLine) >= 0){
      double objPrice = ObjectGetDouble(0,supLine,OBJPROP_PRICE);
      double visibleLows[];
      ArraySetAsSeries(visibleLows,true);
      CopyLow(_Symbol,_Period,1,visible_bars,visibleLows);
      //Print("Object Found & visible bars is: ",ArraySize(visibleLows));
      //ArrayPrint(visibleLows);
      bool matchLowFound = false;
      
      for (int i=0; i<ArraySize(visibleLows); i++){
         if (visibleLows[i] == objPrice){
            Print("> Match price for support found at bar # ",i+1," (",objPrice,")");
            matchLowFound = true;
            break;
         }
      }
      if (!matchLowFound){
         Print("(",objPrice,") > Match price for the support line not found. Delete!");
         deleteLevel(supLine);
      }
   }
   
   static double ResistancePriceTrade = 0;
   if (ObjectFind(0,resLine) >= 0){
      double ResistancePriceLevel = ObjectGetDouble(0,resLine,OBJPROP_PRICE);
      if (ResistancePriceTrade != ResistancePriceLevel){
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
         double open1 = iOpen(_Symbol,_Period,1);
         double high1 = iHigh(_Symbol,_Period,1);
         double low1 = iLow(_Symbol,_Period,1);
         double close1 = iClose(_Symbol,_Period,1);

         if (open1 > close1 && open1 < ResistancePriceLevel
            && high1 > ResistancePriceLevel && Bid < ResistancePriceLevel){
            Print("$$$$$$$$$$$$ SELL NOW SIGNAL!");
            obj_Trade.Sell(0.01,_Symbol,Bid,Bid+350*5*_Point,Bid-350*_Point);
            ResistancePriceTrade = ResistancePriceLevel;
         }
         
      }
   }
   
   static double SupportPriceTrade = 0;
   if (ObjectFind(0,supLine) >= 0){
      double SupportPriceLevel = ObjectGetDouble(0,supLine,OBJPROP_PRICE);
      if (SupportPriceTrade != SupportPriceLevel){
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
         double open1 = iOpen(_Symbol,_Period,1);
         double high1 = iHigh(_Symbol,_Period,1);
         double low1 = iLow(_Symbol,_Period,1);
         double close1 = iClose(_Symbol,_Period,1);

         if (open1 < close1 && open1 > SupportPriceLevel
            && low1 < SupportPriceLevel && Ask > SupportPriceLevel){
            Print("$$$$$$$$$$$$ BUY NOW SIGNAL!");
            obj_Trade.Buy(0.01,_Symbol,Ask,Ask-350*5*_Point,Ask+350*_Point);
            SupportPriceTrade = SupportPriceLevel;
         }
         
      }
   }
   
}
//+------------------------------------------------------------------+

void draw_S_R_Level(string levelName,double price,color clr,int width){
   if (ObjectFind(0,levelName) < 0){
      ObjectCreate(0,levelName,OBJ_HLINE,0,TimeCurrent(),price);
      ObjectSetInteger(0,levelName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,levelName,OBJPROP_WIDTH,width);
   }
   else {
      ObjectSetDouble(0,levelName,OBJPROP_PRICE,price);
   }
   ChartRedraw(0);
}

void deleteLevel(string levelName){
   ObjectDelete(0,levelName);
   ChartRedraw(0);
}

void draw_S_R_Level_Point(string objName,double price,datetime time,
      int arrowcode,int direction,color clr,double angle){
   //objName = " ";
   StringConcatenate(objName,objName," @ \nTime: ",time,"\nPrice: ",DoubleToString(price,_Digits));
   if (ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) {
      ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrowcode);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,10);
      if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
   }
   string prefix = resline_prefix;
   string txt = "\n"+prefix+"("+DoubleToString(price,_Digits)+")";
   string objNameDescription = objName + txt;
   if (ObjectCreate(0,objNameDescription,OBJ_TEXT,0,time,price)) {
     // ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "" + txt);
      ObjectSetInteger(0,objNameDescription,OBJPROP_COLOR,clr);
      ObjectSetDouble(0,objNameDescription,OBJPROP_ANGLE, angle);
      ObjectSetInteger(0,objNameDescription,OBJPROP_FONTSIZE,10);
      if (direction > 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_LEFT);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
      if (direction < 0) {
         ObjectSetInteger(0,objNameDescription,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
         ObjectSetString(0,objNameDescription,OBJPROP_TEXT, "    " + txt);
      }
   }
   ChartRedraw(0);
}

Prost auf uns! Jetzt haben wir ein Handelssystem rein auf der Basis von Preisaktionen entwickelt, das auf der Forex-Handelsstrategie Unterstützung- und Widerstand basiert und nicht nur Handelssignale generiert, sondern auch Marktpositionen auf der Grundlage der generierten Signale eröffnet.


Ergebnisse im Strategietester

Nach einem Test mit dem Strategietester sind die Ergebnisse wie folgt.

  • Salden-/Kapitalkurve:

GRAPH

  • Backtest-Ergebnisse:

ERGEBNISSE


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass die Automatisierung der Unterstützungs- und Widerstandsstrategie für den Devisenhandel möglich und einfach ist, wie wir gesehen haben. Alles, was es braucht, ist ein klares Verständnis der Strategie und ihres Konzepts, um dann das Wissen für einen Durchbruch zu nutzen. Wir haben die leistungsstarken Funktionen der MQL5-Sprache vertrauensvoll für eine präzise und effiziente Handelsstrategie genutzt. Die Analyse und die Erstellung von EAs haben gezeigt, dass die Automatisierung nicht nur wertvolle Zeit spart, sondern auch die Effektivität des Handels erhöht, indem menschliche Fehler und emotionale Störungen reduziert werden.

Haftungsausschluss: Die in diesem Artikel dargestellten Informationen dienen nur zu Lehrzwecken. Er soll lediglich Einblicke in die Erstellung eines „Support and Resistance Expert Advisors“ (EA) geben, der auf einem reinen Preisansatz basiert und somit als Grundlage für die Erstellung eines besseren Expert Advisors mit mehr Optimierung und Datenextraktion verwendet werden sollte. Die dargestellten Informationen garantieren keinen Handelserfolg.

Wir hoffen aufrichtig, dass der Artikel lehrreich und nützlich für Sie bei der Automatisierung von Unterstützung und Widerstand EA war. Diese automatisierte Systemintegration wird mit der weiteren Entwicklung der Finanzmärkte sicherlich zunehmen und den Händlern hochmoderne Instrumente für alle Aspekte der Marktdynamik an die Hand geben. Mit Technologien wie MQL5, die sich ständig weiterentwickeln und die Tür zu komplexeren und intelligenteren Handelslösungen öffnen, scheint die Zukunft des Handels rosig.


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

Automatisierte Parameter-Optimierung für Handelsstrategien mit Python und MQL5 Automatisierte Parameter-Optimierung für Handelsstrategien mit Python und MQL5
Es gibt mehrere Arten von Algorithmen zur Selbstoptimierung von Handelsstrategien und Parametern. Diese Algorithmen werden zur automatischen Verbesserung von Handelsstrategien auf der Grundlage historischer und aktueller Marktdaten eingesetzt. In diesem Artikel werden wir uns eine davon mit Python und MQL5-Beispielen ansehen.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 23): CNNs MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 23): CNNs
Convolutional Neural Networks sind ein weiterer Algorithmus des maschinellen Lernens, der sich darauf spezialisiert hat, mehrdimensionale Datensätze in ihre wichtigsten Bestandteile zu zerlegen. Wir sehen uns an, wie dies typischerweise erreicht wird, und untersuchen eine mögliche Anwendung für Händler in einer anderen Signalklasse des MQL5-Assistenten.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 24): Gleitende Durchschnitte MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 24): Gleitende Durchschnitte
Gleitende Durchschnitte sind ein sehr verbreiteter Indikator, der von den meisten Händlern verwendet und verstanden wird. Wir erforschen mögliche Anwendungsfälle, die in den mit dem MQL5-Assistenten zusammengestellten Expert Advisors vielleicht nicht so häufig vorkommen.
Entwicklung einer Zone Recovery Martingale Strategie in MQL5 Entwicklung einer Zone Recovery Martingale Strategie in MQL5
In diesem Artikel werden die Schritte, die für die Erstellung eines auf dem Zone Recovery-Handelsalgorithmus basierenden Expert Advisors erforderlich sind, ausführlich beschrieben. Dies hilft, das System zu automatisieren und spart den Algotradern Zeit.