English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 2): Das Breakout System Kumo mit Ichimoku und dem Awesome Oscillator

Automatisieren von Handelsstrategien in MQL5 (Teil 2): Das Breakout System Kumo mit Ichimoku und dem Awesome Oscillator

MetaTrader 5Handel |
220 3
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Im vorigen Artikel (Teil 1 der Serie) haben wir gezeigt, wie man das Profitunity-System (Trading Chaos by Will Williams) automatisieren kann. In diesem Artikel (Teil 2) zeigen wir Ihnen, wie Sie die Kumo Breakout Strategy in einen voll funktionsfähigen Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) umwandeln. Die Kumo-Breakout-Strategie verwendet den Indikator Ichimoku Kinko Hyo, um potenzielle Marktumkehrungen und Trendfortsetzungen zu identifizieren, wobei die Kursbewegungen relativ zum Kumo (der Wolke) erfolgen - einer dynamischen Unterstützungs- und Widerstandszone, die von den Linien Senkou Span A und Senkou Span B gebildet wird. Durch die Einbeziehung des Awesome Oszillators als Trendbestätigungsindikator können wir falsche Signale herausfiltern und die Genauigkeit von Handelsein- und -ausstiegen erhöhen. Diese Strategie wird häufig von Händlern eingesetzt, die von starken momentumgetriebenen Marktbewegungen profitieren wollen.

Wir gehen durch den Prozess der Kodierung der Strategielogik, der Verwaltung von Handelsgeschäften und der Verbesserung der Risikokontrolle mit Trailing-Stops. Am Ende dieses Artikels werden Sie genau wissen, wie Sie die Strategie automatisieren, ihre Leistung mit dem MQL5 Strategy Tester testen und sie für optimale Ergebnisse verfeinern können. Zum besseren Verständnis haben wir den Prozess in folgende Abschnitte unterteilt.

  1. Überblick über die Kumo-Breakout-Strategie
  2. Implementierung der Kumo-Breakout-Strategie in MQL5
  3. Testen und Optimieren der Strategie
  4. Schlussfolgerung


Überblick über die Kumo-Breakout-Strategie

Die Kumo-Breakout-Strategie ist eine Trendfolgestrategie, die darauf abzielt, von Kursbewegungen jenseits der Kumo-Wolke zu profitieren. Der Kumo, auch Kumo-Wolke genannt, ist ein schattierter Bereich zwischen den Linien Senkou Span A und Senkou Span B des Ichimoku Kinko Hyo-Indikators, der als dynamisches Unterstützungs- und Widerstandsniveau dient. Wenn der Kurs über den Kumo ausbricht, signalisiert er einen potenziellen Aufwärtstrend, während ein Ausbruch unter den Kumo einen möglichen Abwärtstrend anzeigt. Die für den Indikator verwendeten Parameter sind Tenkan-sen = 8, Kijun-sen = 29 und Senkou-span B = 34. Hier sind die Einstellungen:

DIE ICHIMOKU-EINSTELLUNGEN

Um Fehlsignale herauszufiltern, integriert die Strategie auch den Awesome Oscillator, um eine zusätzliche Bestätigung für Handelseintritte zu liefern. Der Awesome Oscillator identifiziert Momentumsverschiebungen, indem er die Differenz zwischen einem 34-periodischen und einem 5-periodischen einfachen gleitenden Durchschnitt misst, die auf den Medianpreis aufgetragen werden. Kaufsignale werden bestätigt, wenn der Oszillator von negativ nach positiv kreuzt, und Verkaufssignale werden bestätigt, wenn er von positiv nach negativ kreuzt. Durch die Kombination von Kumo-Ausbrüchen mit der Momentum-Bestätigung durch den Awesome Oscillator zielt die Strategie darauf ab, Fehlsignale zu reduzieren und die Wahrscheinlichkeit erfolgreicher Handelsgeschäften zu erhöhen.

In der vollständigen Kombination ergibt sich das unten abgebildete Bild.

ERÖFFNUNGBEDINGUNGEN

Für den Ausstieg aus den Positionen nutzen wir die Logik der Momentumsverschiebung. Wenn der Oszillator vom positiven in den negativen Bereich wechselt, deutet dies auf eine Veränderung des Aufwärts-Momentums hin, und wir schließen die bestehenden Kaufpositionen. Ähnlich verhält es sich, wenn der Oszillator von einem negativen in einen positiven Bereich wechselt: Wir schließen die bestehenden Verkaufspositionen. Hier ist eine Illustration.

AUSSTIEGSBEDINGUNGEN

Dieser Ansatz eignet sich besonders gut für Märkte, die einen Trend aufweisen und eine starke Dynamik aufweisen. In Phasen der Konsolidierung kann die Strategie jedoch aufgrund der sprunghafte Kursbewegung innerhalb des Kumo und des Oszillators falsche Signale erzeugen. Infolgedessen können wir zusätzliche Filter oder Risikomanagementtechniken wie Trailing-Stops anwenden, um potenzielle Drawdowns zu mindern. Das Verständnis dieser Grundprinzipien ist für die erfolgreiche Umsetzung der Strategie als automatisierter Expert Advisor unerlässlich.


Implementierung der Kumo-Breakout-Strategie in MQL5

Nachdem wir alle Theorien über die Kumo-Breakout-Handelsstrategie gelernt haben, wollen wir die Theorie automatisieren und einen Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für den MetaTrader 5 erstellen.

Um einen Expert Advisor (EA) zu erstellen, klicken Sie in Ihrem MetaTrader 5-Terminal auf die Registerkarte Extras 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 Umgebung des MetaQuotes-Spracheditors geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht. Sobald der MetaEditor geöffnet ist, navigieren wir in der Symbolleiste zur Registerkarte „Datei“ und wählen „Neue Datei“, oder drücken einfach die Tastenkombination STRG + N, um ein neues Dokument zu erstellen. Alternativ könnten wir auch auf das Symbol „New“ auf der Registerkarte Werkzeuge klicken. Daraufhin erscheint ein Popup-Fenster des MQL-Assistenten.

In dem sich öffnenden Assistenten markieren wir die Option Expert Advisor (Template bzw. Vorlage) und klicken auf Weiter (Next). Wir geben in den allgemeinen Eigenschaften des Expert Advisors unter dem Abschnitt Name den Dateinamen Ihres Experten an. Nicht vergessen, 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.

NEUER 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 definieren wir einige Metadaten über den Expert Advisor (EA). Dazu gehören der Name des EA, die Copyright-Informationen und ein Link zur MetaQuotes-Website. Wir geben auch die Version des EA an, die auf „1.00“ eingestellt ist.

//+------------------------------------------------------------------+
//|                                          1. Kumo Breakout EA.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"

Dadurch werden beim Laden des Programms die System-Metadaten angezeigt. Anschließend können wir einige globale Variablen hinzufügen, die wir im Programm verwenden werden. Zunächst binden wir eine Handelsinstanz ein, indem wir #include am Anfang des Quellcodes verwenden. Dadurch erhalten wir Zugriff auf die Klasse „CTrade“, die wir zur Erstellung eines Handelsobjekts verwenden werden. Dies ist von entscheidender Bedeutung, da wir sie zur Eröffnung von Handelsgeschä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 verwendet werden soll (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 Einbindungen 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

Danach müssen wir mehrere wichtige Indikator-Handles deklarieren, die wir im Handelssystem verwenden werden.

int handle_Kumo = INVALID_HANDLE;                   //--- Initialize the Kumo indicator handle to an invalid state
int handle_AO = INVALID_HANDLE;                     //--- Initialize the Awesome Oscillator handle to an invalid state

Hier deklarieren wir zwei Integer-Variablen, „handle_Kumo“ und „handle_AO“, die wir verwenden, um die Handles für den Kumo-Indikator (Ichimoku) bzw. den Awesome Oscillator (AO) zu speichern. Wir initialisieren beide Variablen mit dem Wert INVALID_HANDLE, einer vordefinierten Konstante in MQL5, die ein ungültiges oder nicht initialisiertes Handle darstellt. Dies ist wichtig, denn wenn wir einen Indikator erstellen, gibt das System ein Handle zurück, mit dem wir mit dem Indikator interagieren können. Wenn der Handle „INVALID_HANDLE“ ist, ist die Erstellung des Indikators fehlgeschlagen oder wurde nicht korrekt initialisiert. Indem wir die Handles zunächst auf INVALID_HANDLE setzen, stellen wir sicher, dass wir später auf Initialisierungsprobleme prüfen und Fehler entsprechend behandeln können.

Als Nächstes müssen wir Arrays initialisieren, in denen wir die abgerufenen Werte speichern.

double senkouSpan_A[];                              //--- Array to store Senkou Span A values
double senkouSpan_B[];                              //--- Array to store Senkou Span B values

double awesome_Oscillator[];                        //--- Array to store Awesome Oscillator values

Wieder deklarieren wir im globalen Bereich drei Arrays: „senkouSpan_A“, „senkouSpan_B“ und „awesome_Oscillator“, in denen wir die Werte der Senkou-Spanne A, der Senkou-Spanne B bzw. des Awesome-Oszillators speichern. Wir definieren diese Arrays als Double-Typen, d. h. sie enthalten Fließkommawerte, was sich für die Speicherung der Ergebnisse von Indikatorberechnungen eignet. Wir verwenden die Arrays „senkouSpan_A“ und „senkouSpan_B“, um die Werte der Komponenten Senkou Span A und B des Ichimoku-Indikators zu speichern. Im Gegensatz dazu speichert das Array „awesome_Oscillator“ die vom Awesome Oscillator berechneten Werte. Indem wir diese Arrays deklarieren, bereiten wir die Speicherung der Indikatorwerte vor, sodass wir später darauf zugreifen und sie in unserer Handelslogik verwenden können.

Das sind alle Variablen, die wir im globalen Bereich benötigen. Jetzt können wir die Handles des Indikators in OnInit initialisieren, eine Funktion, in der die Initialisierungsschleife abgearbeitet werden.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   //---

   return(INIT_SUCCEEDED);                          //--- Return successful initialization
}

Dies ist eine Ereignisbehandlung, die immer dann aufgerufen wird, wenn der Indikator aus irgendeinem Grund initialisiert wird. Darin initialisieren wir die Indikator-Handles. Wir beginnen mit dem Kumo-Griff.

//--- Initialize the Ichimoku Kumo indicator
handle_Kumo = iIchimoku(_Symbol,_Period,8,29,34);

if (handle_Kumo == INVALID_HANDLE){              //--- Check if Kumo indicator initialization failed
   Print("ERROR: UNABLE TO INITIALIZE THE KUMO INDICATOR HANDLE. REVERTING NOW!"); //--- Log error
   return (INIT_FAILED);                         //--- Return initialization failure
}

Hier initialisieren wir „handle_Kumo“, indem wir die Funktion iIchimoku aufrufen, die eine Instanz des Ichimoku Kumo-Indikators für das aktuelle Symbol (_Symbol) und die aktuelle Periode (_Period) erstellt. Wir verwenden die spezifischen Parameter für den Ichimoku-Indikator: 8, 29 und 34 Perioden für den Tenkan-sen, Kijun-sen bzw. die Senkou-Spanne B, wie bereits dargestellt.

Nach dem Aufruf von iIchimoku gibt die Funktion ein Handle zurück, das wir in „handle_Kumo“ speichern. Dann wird geprüft, ob „handle_Kumo“ gleich INVALID_HANDLE ist, was bedeuten würde, dass die Initialisierung des Indikators fehlgeschlagen ist. Wenn das Handle ungültig ist, wird mit der Funktion „Print“ eine Fehlermeldung mit Angabe des Fehlergrundes protokolliert und die Konstante INIT_FAILED zurückgegeben, die signalisiert, dass der Initialisierungsprozess nicht erfolgreich war. In ähnlicher Weise initialisieren wir den Oszillator-Indikator.

//--- Initialize the Awesome Oscillator
handle_AO = iAO(_Symbol,_Period);

if (handle_AO == INVALID_HANDLE){                //--- Check if AO indicator initialization failed
   Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR HANDLE. REVERTING NOW!"); //--- Log error
   return (INIT_FAILED);                         //--- Return initialization failure
}

Um den Oszillator zu initialisieren, rufen wir die Funktion iAO auf und übergeben nur das Symbol und die Periode als Standardparameter. Wir fahren dann mit dem Rest der Initialisierungslogik fort und verwenden dabei das gleiche Format wie das Kumo-Handle. Nachdem die Initialisierung abgeschlossen ist, können wir mit der Einstellung der Speicherfelder als Zeitreihen fortfahren.

ArraySetAsSeries(senkouSpan_A,true);             //--- Set Senkou Span A array as a time series
ArraySetAsSeries(senkouSpan_B,true);             //--- Set Senkou Span B array as a time series
ArraySetAsSeries(awesome_Oscillator,true);       //--- Set Awesome Oscillator array as a time series

Wir verwenden die Funktion ArraySetAsSeries, um die Arrays „senkouSpan_A“, „senkouSpan_B“ und „awesome_Oscillator“ als Zeitreihen-Arrays zu setzen. Indem wir diese Arrays als Zeitreihen festlegen, stellen wir sicher, dass die jüngsten Werte am Anfang des Arrays gespeichert werden, während ältere Werte an das Ende wandern. Dies ist wichtig, weil Zeitreihendaten in MQL5 normalerweise so organisiert sind, dass auf die neuesten Werte zuerst zugegriffen wird (bei Index 0), was es einfacher macht, die neuesten Daten für Handelsentscheidungen abzurufen.

Wir rufen ArraySetAsSeries für jedes Array auf und übergeben true als zweites Argument, um dieses Zeitreihenverhalten zu aktivieren. Auf diese Weise können wir mit den Daten auf eine Weise arbeiten, die mit typischen Handelsstrategien übereinstimmt, bei denen wir oft zuerst auf die neuesten Werte zugreifen müssen. Wenn die Initialisierung abgeschlossen ist, können wir eine Meldung in das Journal drucken, um die Bereitschaft anzuzeigen.

Print("SUCCESS. ",__FILE__," HAS BEEN INITIALIZED."); //--- Log successful initialization

Nach erfolgreicher Initialisierung verwenden wir die Funktion Print, um eine Meldung zu protokollieren, die angibt, dass der Initialisierungsprozess erfolgreich war. Die Meldung enthält die Zeichenkette „SUCCESS.“, gefolgt von der speziellen vordefinierten Variablen __FILE__, die den Namen der aktuellen Quellcodedatei darstellt. Durch die Verwendung von __FILE__ können wir den Dateinamen dynamisch in die Protokollmeldung einfügen, was bei größeren Projekten mit mehreren Dateien bei der Fehlersuche oder der Verfolgung des Initialisierungsprozesses helfen kann. Die Meldung wird auf dem Terminal oder in der Protokolldatei ausgedruckt und bestätigt, dass die Initialisierung erfolgreich abgeschlossen wurde. Mit diesem Schritt wird sichergestellt, dass wir eine angemessene Rückmeldung über den Status des Initialisierungsprozesses erhalten, was es einfacher macht, mögliche Probleme im Code zu erkennen.

Der vollständige Initialisierungscode lautet wie folgt:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   //--- Initialize the Ichimoku Kumo indicator
   handle_Kumo = iIchimoku(_Symbol,_Period,8,29,34);
   
   if (handle_Kumo == INVALID_HANDLE){              //--- Check if Kumo indicator initialization failed
      Print("ERROR: UNABLE TO INITIALIZE THE KUMO INDICATOR HANDLE. REVERTING NOW!"); //--- Log error
      return (INIT_FAILED);                         //--- Return initialization failure
   }
   
   //--- Initialize the Awesome Oscillator
   handle_AO = iAO(_Symbol,_Period);
   
   if (handle_AO == INVALID_HANDLE){                //--- Check if AO indicator initialization failed
      Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR HANDLE. REVERTING NOW!"); //--- Log error
      return (INIT_FAILED);                         //--- Return initialization failure
   }

   ArraySetAsSeries(senkouSpan_A,true);             //--- Set Senkou Span A array as a time series
   ArraySetAsSeries(senkouSpan_B,true);             //--- Set Senkou Span B array as a time series
   ArraySetAsSeries(awesome_Oscillator,true);       //--- Set Awesome Oscillator array as a time series
   
   Print("SUCCESS. ",__FILE__," HAS BEEN INITIALIZED."); //--- Log successful initialization
   
   //---
   return(INIT_SUCCEEDED);                          //--- Return successful initialization
}

Dies ergibt die folgende Ausgabe.

INIT NACHRICHT

Da wir Datenspeicher-Arrays und Handles initialisiert haben, wollen wir sie nicht beibehalten, wenn wir das Programm deinitialisieren, da wir damit unnötige Ressourcen belegt haben. Wir handhaben dies mit der Ereignisbehandlung von OnDeinit, die immer dann aufgerufen wird, wenn das Programm deinitialisiert wird, egal aus welchem Grund.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   //--- Free memory allocated for Senkou Span A and B arrays
   ArrayFree(senkouSpan_A);
   ArrayFree(senkouSpan_B);
   
   //--- Free memory allocated for the Awesome Oscillator array
   ArrayFree(awesome_Oscillator);
}

In der Funktion OnDeinit führen wir Aufräumarbeiten durch, um den während des Initialisierungsprozesses zugewiesenen Speicher freizugeben. Konkret verwenden wir die Funktion ArrayFree, um den Speicher für die Arrays „senkouSpan_A“, „senkouSpan_B“ und „awesome_Oscillator“ freizugeben. Diese Arrays wurden zuvor verwendet, um die Werte des Ichimoku Kumo-Indikators und des Awesome Oscillators zu speichern. Da sie nun nicht mehr benötigt werden, geben wir den Speicher frei, um Ressourcenlecks zu vermeiden. Auf diese Weise stellen wir sicher, dass das Programm die Systemressourcen effizient verwaltet und unnötigen Speicherverbrauch vermeidet, wenn der Expertenberater nicht mehr aktiv ist.

Jetzt muss nur noch die Handelslogik gehandhabt werden, bei der wir die Indikatorwerte abrufen und analysieren, um Handelsentscheidungen zu treffen. Dies geschieht in der Ereignisbehandlung von OnTick, die immer dann aufgerufen wird, wenn es einen neuen Tick gibt oder sich der Preis einfach ändert. Der erste Schritt, den wir tun müssen, besteht darin, Datenpunkte aus den Indikator-Handles abzurufen und sie für die weitere Analyse zu speichern.

//--- Copy data for Senkou Span A from the Kumo indicator
if (CopyBuffer(handle_Kumo,SENKOUSPANA_LINE,0,2,senkouSpan_A) < 2){
   Print("ERROR: UNABLE TO COPY REQUESTED DATA FROM SENKOUSPAN A LINE. REVERTING NOW!"); //--- Log error
   return;                                        //--- Exit if data copy fails
}
//--- Copy data for Senkou Span B from the Kumo indicator
if (CopyBuffer(handle_Kumo,SENKOUSPANB_LINE,0,2,senkouSpan_B) < 2){
   Print("ERROR: UNABLE TO COPY REQUESTED DATA FROM SENKOUSPAN B LINE. REVERTING NOW!"); //--- Log error
   return;                                        //--- Exit if data copy fails
}

Hier verwenden wir die Funktion CopyBuffer, um Daten aus den Zeilen „Senkou Span A“ und „Senkou Span B“ des Kumo-Indikators (Ichimoku) in die Arrays „senkouSpan_A“ bzw. „senkouSpan_B“ zu kopieren. Das erste Argument, das an CopyBuffer übergeben wird, ist der Indikator-Handle „handle_Kumo“, der sich auf den initialisierten Kumo-Indikator bezieht. Das zweite Argument gibt an, welche Datenzeile kopiert werden soll: „SENKOUSPANA_LINE“ für Senkou-Spanne A und „SENKOUSPANB_LINE“ für Senkou-Spanne B. Das dritte Argument ist der Startindex, von dem aus kopiert werden soll, der auf 0 gesetzt wird, um mit den neuesten Daten zu beginnen. Das vierte Argument gibt die Anzahl der zu kopierenden Datenpunkte an, die in diesem Fall 2 beträgt. Das letzte Argument ist das Array, in dem die Daten gespeichert werden sollen, „senkouSpan_A“ oder „senkouSpan_B“.

Nach dem Aufruf von CopyBuffer prüfen wir, ob die Funktion einen Wert kleiner als 2 zurückgibt, was bedeutet, dass die angeforderten Daten nicht erfolgreich kopiert wurden. In diesem Fall wird mit der Funktion Print eine Fehlermeldung protokolliert, die besagt, dass die Daten nicht aus der entsprechenden Senkou-Span-Zeile übernommen werden konnten, und die Funktion wird mit Return beendet. Dadurch wird sichergestellt, dass bei einem Fehlschlagen der Datenkopie der Fehler ordnungsgemäß behandelt wird, indem das Problem protokolliert und die weitere Ausführung der Funktion gestoppt wird.

Wir verwenden dieselbe Logik, um die Oszillatorwerte abzurufen.

//--- Copy data from the Awesome Oscillator
if (CopyBuffer(handle_AO,0,0,3,awesome_Oscillator) < 3){
   Print("ERROR: UNABLE TO COPY REQUESTED DATA FROM AWESOME OSCILLATOR. REVERTING NOW!"); //--- Log error
   return;                                        //--- Exit if data copy fails
}

Wir verwenden die Funktion CopyBuffer, um Daten aus dem Awesome Oscillator (AO) in das Array „awesome_Oscillator“ zu kopieren. Das erste Argument, das an die Funktion CopyBuffer übergeben wird, ist das Indikator-Handle „handle_AO“, der sich auf den initialisierten Awesome Oscillator bezieht. Das zweite Argument gibt den zu kopierenden Datenzeilen- oder Pufferindex an, der in diesem Fall 0 ist, da der Awesome Oscillator einen einzigen Datenpuffer hat. Das dritte Argument ist der Startindex, der auf 0 gesetzt wird, um mit dem Kopieren der letzten Daten zu beginnen. Das vierte Argument gibt die Anzahl der zu kopierenden Datenpunkte an, die in diesem Fall auf 3 gesetzt ist, was bedeutet, dass wir die letzten drei Werte kopieren wollen. Das letzte Argument ist das Array „awesome_Oscillator“, in dem die kopierten Daten gespeichert werden. Wenn die abgerufenen Daten weniger als die angeforderten sind, wird eine Fehlermeldung protokolliert und zurückgegeben.

Wenn wir alle erforderlichen Daten haben, können wir sie weiterverarbeiten. Als Erstes müssen wir eine Logik definieren, mit der wir sicherstellen, dass wir die Daten einmal analysieren, wenn ein neuer vollständiger Balken erzeugt wird, und nicht bei jedem Tick. Wir bauen diese Logik in eine Funktion ein.

//+------------------------------------------------------------------+
//|   IS NEW BAR FUNCTION                                            |
//+------------------------------------------------------------------+
bool isNewBar(){ 
   static int prevBars = 0;                         //--- Store previous bar count
   int currBars = iBars(_Symbol,_Period);           //--- Get current bar count for the symbol and period
   if (prevBars == currBars) return (false);        //--- If bars haven't changed, return false
   prevBars = currBars;                             //--- Update previous bar count
   return (true);                                   //--- Return true if new bar is detected
}

Wir definieren eine boolesche Funktion „isNewBar“, mit der festgestellt werden kann, ob für das angegebene Symbol und den angegebenen Zeitraum ein neuer Balken im Diagramm erschienen ist. Innerhalb dieser Funktion deklarieren wir eine statische Variable „prevBars“, die die Anzahl der Balken der vorherigen Prüfung speichert. Das Schlüsselwort static sorgt dafür, dass die Variable ihren Wert zwischen Funktionsaufrufen beibehält.

Dann verwenden wir die Funktion iBars, um die aktuelle Anzahl der Balken im Diagramm für das angegebene Symbol (_Symbol) und den Zeitraum (_Period) zu ermitteln. Das Ergebnis wird in der Variablen „currBars“ gespeichert. Wenn sich die Anzahl der Balken nicht geändert hat (d. h. „prevBars“ ist gleich „currBars“), wird false zurückgegeben, was bedeutet, dass kein neuer Balken erschienen ist. Wenn sich die Anzahl der Balken geändert hat, aktualisieren wir „prevBars“ mit der aktuellen Balkenanzahl und geben „true“ zurück, um zu signalisieren, dass ein neuer Balken erkannt worden ist. Mit dieser Funktion bewaffnet, können wir sie innerhalb der Ereignisbehandlung der Ticks aufrufen und analysieren.

//--- Check if a new bar has formed
if (isNewBar()){
   //--- Determine if the AO has crossed above or below zero
   bool isAO_Above = awesome_Oscillator[1] > 0 && awesome_Oscillator[2] < 0;
   bool isAO_Below = awesome_Oscillator[1] < 0 && awesome_Oscillator[2] > 0;

//---
}

Hier prüfen wir, ob sich ein neuer Balken gebildet hat, indem wir die Funktion „isNewBar“ aufrufen. Wenn ein neuer Balken erkannt wird (d.h. „isNewBar“ gibt true zurück), fahren wir fort, das Verhalten des Awesome Oscillators (AO) zu bestimmen.

Wir definieren zwei boolesche Variablen: „isAO_Above“ und „isAO_Below“. Die Variable „isAO_Above“ wird auf „true“ gesetzt, wenn der vorherige Wert des Awesome Oscillators (awesome_Oscillator[1]) größer als Null ist und der Wert davor (awesome_Oscillator[2]) kleiner als Null ist. Bei dieser Bedingung wird geprüft, ob der AO-Wert die Nulllinie überschritten hat, was auf ein potenzielles Aufwärtssignal hinweist. In ähnlicher Weise wird „isAO_Below“ auf „true“ gesetzt, wenn der vorherige AO-Wert (awesome_Oscillator[1]) kleiner als Null und der davor liegende Wert (awesome_Oscillator[2]) größer als Null ist, was darauf hindeutet, dass der AO-Wert unter Null gefallen ist, was eine Abwärtsbewegung signalisieren könnte. Wir können dann die gleiche Methode verwenden, um die andere Logik einzustellen.

//--- Determine if the Kumo is bullish or bearish
bool isKumo_Above = senkouSpan_A[1] > senkouSpan_B[1];
bool isKumo_Below = senkouSpan_A[1] < senkouSpan_B[1];
      
//--- Determine buy and sell signals based on conditions
bool isBuy_Signal = isAO_Above && isKumo_Below && getClosePrice(1) > senkouSpan_A[1] && getClosePrice(1) > senkouSpan_B[1];
bool isSell_Signal = isAO_Below && isKumo_Above && getClosePrice(1) < senkouSpan_A[1] && getClosePrice(1) < senkouSpan_B[1];

Hier bestimmen wir die Bedingungen für ein Auf- oder Abwärts-Setup des Kumo (Ichimoku). Zunächst definieren wir zwei boolesche Variablen: „isKumo_Above“ und „isKumo_Below“. Die Variable „isKumo_Above“ wird auf „true“ gesetzt, wenn der vorherige Wert der Senkou-Spanne A (senkouSpan_A[1]) größer ist als der vorherige Wert der Senkou-Spanne B (senkouSpan_B[1]), was auf einen Aufwärts-Kumo (steigende Marktstimmung) hinweist. Andererseits wird „isKumo_Below“ auf „true“ gesetzt, wenn die Senkou-Spanne A kleiner ist als die Senkou-Spanne B, was auf ein Abwärts-Kumo (fallende Marktstimmung) hinweist.

Als Nächstes System definieren wir die Bedingungen für potenzielle Kauf- und Verkaufssignale. Das Kaufsignal („isBuy_Signal“) wird auf „true“ gesetzt, wenn die folgenden Bedingungen erfüllt sind: Der Awesome Oscillator hat die Nulllinie überschritten (isAO_Above), der Kumo fällt (isKumo_Below), und der Schlusskurs des vorangegangenen Balkens liegt sowohl über der Senkou Span A als auch über der Senkou Span B. Dies deutet auf eine potenzielle Aufwärtsbewegung des Kurses trotz des Abwärts-Kumo hin. Das Verkaufssignal („isSell_Signal“) wird auf true gesetzt, wenn der Awesome Oscillator unter Null gekreuzt hat (isAO_Below), der Kumo steigt (isKumo_Above) und der Schlusskurs des vorherigen Balkens sowohl unter Senkou Span A als auch unter Senkou Span B liegt.

Sie haben vielleicht bemerkt, dass wir eine neue Funktion zur Ermittlung der Schlusskurse verwendet haben. Hier ist die Logik aller Funktionen, die wir brauchen werden.

//+------------------------------------------------------------------+
//|        FUNCTION TO GET CLOSE PRICES                              |
//+------------------------------------------------------------------+
double getClosePrice(int bar_index){
   return (iClose(_Symbol, _Period, bar_index));    //--- Retrieve the close price of the specified bar
}

//+------------------------------------------------------------------+
//|        FUNCTION TO GET ASK PRICES                                |
//+------------------------------------------------------------------+
double getAsk(){
   return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits)); //--- Get and normalize the Ask price
}

//+------------------------------------------------------------------+
//|        FUNCTION TO GET BID PRICES                                |
//+------------------------------------------------------------------+
double getBid(){
   return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits)); //--- Get and normalize the Bid price
}

Hier definieren wir drei Funktionen, um verschiedene Arten von Preisen abzurufen:

  • Funktion „getClosePrice“: Diese Funktion ruft den Schlusskurs eines bestimmten Balkens ab. Sie benötigt einen Parameter „bar_index“, der den Index des Balkens angibt, für den wir den Schlusskurs ermitteln wollen. Die Funktion ruft die integrierte Funktion iClose auf und übergibt das Symbol (_Symbol), die Periode (_Period) und den Balkenindex, um den Schlusskurs des angegebenen Balkens zu ermitteln. Der ermittelte Preis wird als Double zurückgegeben.
  • Funktion „getAsk“: Mit dieser Funktion wird der aktuelle Ask-Kurs für das angegebene Symbol ermittelt. Sie verwendet die Funktion SymbolInfoDouble mit der Konstante SYMBOL_ASK, um den Briefkurs zu ermitteln. Das Ergebnis wird dann mit der Funktion NormalizeDouble normalisiert, um sicherzustellen, dass der Preis auf die richtige Anzahl von Dezimalstellen gerundet wird, die auf der Eigenschaft _Digits des Symbols basieren. Diese Funktion gibt den normalisierten Briefkurs als Double zurück.
  • Funktion „getBid“: Diese Funktion ruft den aktuellen Geldkurs für das angegebene Symbol ab. Ähnlich wie die Funktion „getAsk“ verwendet sie SymbolInfoDouble mit der Konstanten SYMBOL_BID, um den Geldkurs zu erhalten, und normalisiert ihn dann mit der Funktion NormalizeDouble, um sicherzustellen, dass er der korrekten Genauigkeit entspricht, die durch die Eigenschaft _Digits des Symbols definiert ist. Diese Funktion gibt den normalisierten Geldkurs als Double zurück.

Diese Funktionen bieten eine einfache Möglichkeit, die relevanten Preise für Handelsentscheidungen innerhalb des Programms abzurufen und zu normalisieren. Wir können dann die berechneten Handelssignale verwenden und entsprechende Positionen für die vorhandenen Signale eröffnen.

if (isBuy_Signal){                            //--- If buy signal is generated
   Print("BUY SIGNAL GENERATED @ ",iTime(_Symbol,_Period,1),", PRICE: ",getAsk()); //--- Log buy signal
   obj_Trade.Buy(0.01,_Symbol,getAsk());      //--- Execute a buy trade
}
else if (isSell_Signal){                      //--- If sell signal is generated
   Print("SELL SIGNAL GENERATED @ ",iTime(_Symbol,_Period,1),", PRICE: ",getBid()); //--- Log sell signal
   obj_Trade.Sell(0.01,_Symbol,getBid());     //--- Execute a sell trade
}

Wir prüfen, ob ein Kauf- oder Verkaufssignal generiert wurde und führen den entsprechenden Handel aus. Wenn „isBuy_Signal“ wahr ist, was bedeutet, dass ein Kaufsignal aufgetreten ist, protokollieren wir das Ereignis zunächst mit der Funktion Print. Wir fügen den Zeitstempel des vorangegangenen Balkens, der mit der Funktion iTime abgerufen wird, und den aktuellen Briefkurs, der mit der Funktion „getAsk“ ermittelt wird, ein. Dieses Protokoll enthält eine Aufzeichnung des Kaufsignals und des Preises, zu dem es aufgetreten ist. Nach der Protokollierung führen wir den Kauf aus, indem wir „obj_Trade.Buy(0.01, _Symbol, getAsk())“ aufrufen, wodurch ein Kaufauftrag für 0,01 Lots zum aktuellen Briefkurs erteilt wird.

Wenn „isSell_Signal“ wahr ist, was auf ein Verkaufssignal hinweist, protokollieren wir das Ereignis mit der Funktion Print, die den Zeitstempel des vorherigen Balkens und den aktuellen Geldkurs aus der Funktion „getBid“ enthält. Nach der Protokollierung platzieren wir einen Verkaufsauftrag mit „obj_Trade.Sell(0.01, _Symbol, getBid())“, der einen Verkaufsauftrag für 0,01 Lots zum aktuellen Geldkurs ausführt. So wird sichergestellt, dass immer dann gehandelt wird, wenn die Bedingungen für Kauf- oder Verkaufssignale erfüllt sind, und wir führen eine klare Aufzeichnung über diese Aktionen.

Schließlich müssen wir nur noch die Schwungverschiebungen überprüfen und die entsprechenden Positionen schließen. Hier ist die Logik.

if (isAO_Above || isAO_Below){                //--- If AO crossover occurs
   if (PositionsTotal() > 0){                 //--- If there are open positions
      for (int i=PositionsTotal()-1; i>=0; i--){ //--- Loop through open positions
         ulong posTicket = PositionGetTicket(i); //--- Get the position ticket
         if (posTicket > 0){                  //--- If ticket is valid
            if (PositionSelectByTicket(posTicket)){ //--- Select position by ticket
               ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get position type
               if (posType == POSITION_TYPE_BUY){ //--- If position is a buy
                  if (isAO_Below){            //--- If AO indicates bearish crossover
                     Print("CLOSING THE BUY POSITION WITH #",posTicket); //--- Log position closure
                     obj_Trade.PositionClose(posTicket); //--- Close the buy position
                  }
               }
               else if (posType == POSITION_TYPE_SELL){ //--- If position is a sell
                  if (isAO_Above){            //--- If AO indicates bullish crossover
                     Print("CLOSING THE SELL POSITION WITH #",posTicket); //--- Log position closure
                     obj_Trade.PositionClose(posTicket); //--- Close the sell position
                  }
               }
            }
         }
      }
   }
}

Hier prüfen wir, ob der Awesome Oscillator (AO) über oder unter Null kreuzt, und verwalten offene Positionen entsprechend. Wenn entweder „isAO_Above“ oder „isAO_Below“ wahr ist, was bedeutet, dass ein AO-Crossover stattgefunden hat, prüfen wir, ob es offene Positionen gibt, indem wir die Funktion PositionsTotal aufrufen. Wenn es offene Positionen gibt (d.h. „PositionsTotal“ liefert einen Wert größer als 0), werden alle offenen Positionen in einer Schleife durchlaufen, beginnend mit der jüngsten Position (PositionsTotal()-1) und rückwärts.

Innerhalb der Schleife rufen wir das Positionsticket mit der Funktion PositionGetTicket ab. Wenn das Positionsticket gültig ist (d. h. größer als 0), wird die Position mit der Funktion PositionSelectByTicket ausgewählt. Anschließend wird der Positionstyp durch den Aufruf von PositionGetInteger ermittelt. Wenn es sich um eine Kaufposition handelt (POSITION_TYPE_BUY), prüfen wir, ob „isAO_Below“ wahr ist, was auf ein Abwärtskreuz hinweist. Wenn ja, protokollieren wir die Schließung der Kaufposition mit der Funktion Print und schließen die Position mit „obj_Trade.PositionClose(posTicket)“.

Wenn es sich bei der Position um einen Verkauf handelt (POSITION_TYPE_SELL), wird geprüft, ob „isAO_Above“ wahr ist, was auf ein Aufwärtskreuz hinweist. Wenn ja, protokollieren wir die Schließung der Verkaufsposition und schließen sie mit „obj_Trade.PositionClose(posTicket)“. Dadurch wird sichergestellt, dass wir offene Positionen effektiv verwalten und sie schließen, wenn die Bedingungen für einen AO-Crossover eine Verschiebung der Marktdynamik signalisieren. Nach der Ausführung des Programms erhalten wir die folgende Ausgabe.

Bestätigung der Verkaufsposition.

BESTÄTIGUNG DER VERKAUFSPOSITION

Bestätigung des Ausstiegs aus einer Verkaufsposition bei einem Wechsel des Marktmomentums:

VERKAUFSPOSITION BEENDEN

Anhand der obigen Abbildungen können wir sicher sein, dass wir unsere gewünschten Ziele erreicht haben. Wir können nun mit dem Testen und Optimieren des Programms fortfahren. Dies wird im nächsten Abschnitt behandelt.


Testen und Optimieren der Strategie

In diesem Abschnitt testen wir die Strategie und optimieren sie, damit sie unter verschiedenen Marktbedingungen besser funktioniert. Die Änderung, die wir vornehmen werden, betrifft den Bereich des Risikomanagements, wo wir einen Trailing-Stop hinzufügen können, um Gewinne zu sichern, wenn wir bereits im Gewinn sind, anstatt darauf zu warten, dass der Markt eine vollständige Entscheidung über die Verschiebung der Marktdynamik trifft. Um dies effizient zu handhaben, werden wir eine dynamische Funktion zur Handhabung der Trailing-Stop-Logik konstruieren.

//+------------------------------------------------------------------+
//|        FUNCTION TO APPLY TRAILING STOP                           |
//+------------------------------------------------------------------+
void applyTrailingSTOP(double slPoints, CTrade &trade_object,int magicNo=0){
   double buySL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID)-slPoints,_Digits); //--- Calculate SL for buy positions
   double sellSL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK)+slPoints,_Digits); //--- Calculate SL for sell positions

   for (int i = PositionsTotal() - 1; i >= 0; i--){ //--- Iterate through all open positions
      ulong ticket = PositionGetTicket(i);          //--- Get position ticket
      if (ticket > 0){                              //--- If ticket is valid
         if (PositionGetString(POSITION_SYMBOL) == _Symbol &&
            (magicNo == 0 || PositionGetInteger(POSITION_MAGIC) == magicNo)){ //--- Check symbol and magic number
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
               buySL > PositionGetDouble(POSITION_PRICE_OPEN) &&
               (buySL > PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == 0)){ //--- Modify SL for buy position if conditions are met
               trade_object.PositionModify(ticket,buySL,PositionGetDouble(POSITION_TP));
            }
            else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
               sellSL < PositionGetDouble(POSITION_PRICE_OPEN) &&
               (sellSL < PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == 0)){ //--- Modify SL for sell position if conditions are met
               trade_object.PositionModify(ticket,sellSL,PositionGetDouble(POSITION_TP));
            }
         }
      }
   }
}

Hier implementieren wir eine Funktion zur Anwendung eines Trailing-Stops auf offene Positionen. Die Funktion heißt „applyTrailingSTOP“ und benötigt drei Parameter: „slPoints“, der die Anzahl der Punkte angibt, die für den Stop-Loss gesetzt werden sollen; „trade_object“, ein Verweis auf das Handelsobjekt, das zur Änderung der Positionen verwendet wird; und eine optionale „magicNo“, die dazu dient, bestimmte Positionen anhand ihrer magischen Zahl zu identifizieren. Zunächst berechnen wir die Stop-Loss (SL) für Kauf- und Verkaufspositionen. Bei Kaufpositionen wird der Stop-Loss auf den Geldkurs abzüglich der angegebenen „slPoints“ gesetzt, bei Verkaufspositionen auf den Briefkurs zuzüglich der angegebenen „slPoints“. Beide SL-Werte werden mit der Funktion NormalizeDouble normalisiert, um der Dezimalgenauigkeit des Symbols zu entsprechen, die durch die Variable _Digits definiert ist.

Als Nächstes durchlaufen wir mit der Funktion PositionsTotal eine Schleife durch alle offenen Positionen, wobei wir von der jüngsten zur ältesten Position iterieren. Für jede Position rufen wir das Positionsticket mit der Funktion PositionGetTicket ab und stellen sicher, dass es gültig ist. Dann wird geprüft, ob das Symbol der Position mit dem aktuellen Symbol (_Symbol) übereinstimmt und ob die magische Zahl der Position mit der angegebenen „magicNo“ übereinstimmt, es sei denn, die magische Zahl ist auf 0 gesetzt; in diesem Fall werden alle Positionen berücksichtigt.

Handelt es sich bei der Position um eine Kaufposition (POSITION_TYPE_BUY), wird geprüft, ob der berechnete Kauf-Stop-Loss („buySL“) über dem Eröffnungskurs der Position (POSITION_PRICE_OPEN) liegt und ob er größer ist als der aktuelle Stop-Loss (POSITION_SL) oder ob der aktuelle Stop-Loss nicht gesetzt ist („POSITION_SL“ == 0). Wenn diese Bedingungen erfüllt sind, aktualisieren wir den Stop-Loss der Position, indem wir „trade_object.PositionModify(ticket, buySL, PositionGetDouble(POSITION_TP))“ aufrufen, wodurch der Stop-Loss der Position geändert wird, während der Take-Profit („POSITION_TP“) unverändert bleibt.

Handelt es sich bei der Position um eine Verkaufsposition (POSITION_TYPE_SELL), wenden wir eine ähnliche Logik an. Es wird geprüft, ob der berechnete Verkaufs-Stop-Loss („sellSL“) unter dem Eröffnungskurs der Position (POSITION_PRICE_OPEN) liegt und ob er kleiner ist als der aktuelle Stop-Loss (POSITION_SL) oder ob der aktuelle Stop-Loss nicht gesetzt ist. Wenn diese Bedingungen erfüllt sind, wird der Stop-Loss der Position mit „trade_object.PositionModify(ticket, sellSL, PositionGetDouble(POSITION_TP))“ aktualisiert.

Nachdem wir die Funktion definiert haben, müssen wir sie nur noch über die Tick-Funktion aufrufen, damit sie ausgeführt werden kann. Wir erreichen dies, indem wir sie wie folgt aufrufen und die entsprechenden Parameter übergeben.

if (PositionsTotal() > 0){                       //--- If there are open positions
   applyTrailingSTOP(3000*_Point,obj_Trade,0);  //--- Apply a trailing stop
}

Wenn es Positionen gibt, rufen wir die Funktion „applyTrailingSTOP“ auf, um einen Trailing-Stop auf diese offenen Positionen anzuwenden. Die Funktion wird mit drei Argumenten aufgerufen:

  • Trailing-Stop in Punkten: Der Stop-Loss-Abstand wird berechnet als „3000 * _Point“, wobei _Point die kleinstmögliche Kursbewegung für das aktuelle Symbol darstellt. Das bedeutet, dass der Stop-Loss 3000 Punkte vom aktuellen Marktpreis entfernt gesetzt wird.
  • Handelsobjekt: Wir übergeben „obj_Trade“, eine Instanz des Handelsobjekts, das zur Änderung von Stop-Loss und Take-Profit der Position verwendet wird.
  • Magische Zahl: Das dritte Argument wird auf 0 gesetzt, d.h. die Funktion wendet den Trailing-Stop auf alle offenen Positionen an, unabhängig von ihrer magischen Zahl.

Nach der Anwendung des Trailing-Stopps erhalten wir die folgende Ausgabe.

TRAILING STOP GIF

Aus der Visualisierung geht hervor, dass wir nicht auf eine Verschiebung der Marktdynamik warten, sondern unsere Gewinne festschreiben und die Gewinne maximieren, indem wir den Stop-Loss jedes Mal verschieben, wenn sich der Markt in unsere Richtung bewegt. Die endgültigen Ergebnisse der Strategietester sehen wie folgt aus.

Saldenkurve des Testers:

TESTER-GRAFIK

Testergebnisse:

TESTERGEBNISSE


Schlussfolgerung

Abschließend wurde in diesem Artikel gezeigt, wie man einen MQL5 Expert Advisor (EA) unter Verwendung des Kumo Breakout-Systems erstellt. Durch die Integration des Kumo-Indikators und des Awesome Oszillators (AO) haben wir einen Rahmen geschaffen, um eine Verschiebung des Marktmomentums und die Ausbruchssignale zu erkennen. Zu den wichtigsten Schritten gehörten die Konfiguration von Indikator-Handles, die Extraktion von Schlüsselwerten und die Automatisierung der Handelsausführung mit Trailing-Stops und Positionsmanagement, was zu einem strategiegesteuerten EA mit robuster Handelslogik führte.

Haftungsausschluss: Dieser Artikel ist ein Leitfaden für die Entwicklung von MQL5 EAs, die auf indikatorgesteuerten Handelssignalen basieren. Das Kumo-Breakout-System ist zwar eine beliebte Strategie, ihre Wirksamkeit ist jedoch nicht unter allen Marktbedingungen gewährleistet. Der Handel birgt ein finanzielles Risiko, und frühere Leistungen sind keine Garantie für zukünftige Ergebnisse. Gründliche Tests und ein angemessenes Risikomanagement sind vor dem Live-Handel unerlässlich.

Mit Hilfe dieses Leitfadens können Sie Ihre MQL5-Entwicklungsfähigkeiten verbessern und anspruchsvollere Handelssysteme erstellen. Die hier vorgestellten Konzepte der Indikatorintegration, der Signallogik und der Handelsautomatisierung können auch auf andere Strategien angewandt werden, was zu weiterer Erforschung und Innovation im algorithmischen Handel anregt. Viel Spaß beim Kodieren und ein erfolgreicher Handel!

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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (3)
Javier Santiago Gaston De Iriarte Cabrera
Gut gemacht, Mann!
Allan Munene Mutiiria
Allan Munene Mutiiria | 17 Dez. 2024 in 17:16
Javier Santiago Gaston De Iriarte Cabrera #:
Gut gemacht!

Herzlichen Dank! Ich schätze Ihr freundliches Feedback sehr.

Hely Rojas
Hely Rojas | 17 Dez. 2024 in 22:25
Es ist toll, einen Code zu sehen, der funktioniert und keine neuronalen Netze verwendet. Großartig.
Ich denke, dass der Einfachheit halber heute alles mit Keras und TensorFlow gelöst wird... Aber ist es auch gelöst?
Es ist sehr ermutigend, dass der "alte Weg" der Erstellung eines Expert Advisors immer noch funktioniert.
Ensemble-Methoden zur Verbesserung numerischer Vorhersagen in MQL5 Ensemble-Methoden zur Verbesserung numerischer Vorhersagen in MQL5
In diesem Artikel stellen wir die Implementierung mehrerer Ensemble-Lernmethoden in MQL5 vor und untersuchen ihre Wirksamkeit in verschiedenen Szenarien.
Beherrschen von Dateioperationen in MQL5: Von Basic I/O bis zum Erstellen eines nutzerdefinierten CSV-Readers Beherrschen von Dateioperationen in MQL5: Von Basic I/O bis zum Erstellen eines nutzerdefinierten CSV-Readers
Dieser Artikel konzentriert sich auf wesentliche MQL5-Dateiverarbeitungstechniken, die Handelsprotokolle, CSV-Verarbeitung und externe Datenintegration umfassen. Es bietet sowohl ein konzeptionelles Verständnis als auch praktische Anleitungen zur Programmierung. Der Leser lernt Schritt für Schritt, wie man eine nutzerdefinierte CSV-Importer-Klasse erstellt und erwirbt so praktische Fähigkeiten für reale Anwendungen.
Erstellen von selbstoptimierenden Expertenberatern in MQL5 (Teil 2): USDJPY Scalping Strategie Erstellen von selbstoptimierenden Expertenberatern in MQL5 (Teil 2): USDJPY Scalping Strategie
Seien Sie dabei, wenn wir uns heute der Herausforderung stellen, eine Handelsstrategie rund um das USDJPY-Paar zu entwickeln. Wir handeln Kerzenmuster, die auf dem täglichen Zeitrahmen gebildet werden, weil sie potenziell mehr Kraft hinter sich haben. Unsere anfängliche Strategie war gewinnbringend, was uns ermutigte, die Strategie weiter zu verfeinern und zusätzliche Sicherheitsschichten hinzuzufügen, um das gewonnene Kapital zu schützen.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 4): Der Analytik Forecaster EA Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 4): Der Analytik Forecaster EA
Wir gehen über die einfache Darstellung von analysierten Metriken in Charts hinaus und bieten eine breitere Perspektive, die auch die Integration von Telegram umfasst. Mit dieser Erweiterung können wichtige Ergebnisse über die Telegram-App direkt auf Ihr mobiles Gerät geliefert werden. Begleiten Sie uns in diesem Artikel auf dieser gemeinsamen Reise.