English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 8): Aufbau eines Expert Advisors mit harmonischen Schmetterlingsmustern

Automatisieren von Handelsstrategien in MQL5 (Teil 8): Aufbau eines Expert Advisors mit harmonischen Schmetterlingsmustern

MetaTrader 5Handel |
103 2
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Im vorherigen Artikel (Teil 7) haben wir einen Expert Advisor für einen Rasterhandel in MetaQuotes Language 5 (MQL5) mit dynamischer Losgrößen-Skalierung zur Optimierung von Risiko und Ertrag entwickelt. In Teil 8 konzentrieren wir uns nun auf das harmonische Schmetterlingsmuster - ein Umkehrmuster, das sich präzise Fibonacci Verhältnisse zunutze macht, um potenzielle Wendepunkte im Markt zu erkennen. Dieser Ansatz hilft nicht nur, klare Ein- und Ausstiegssignale zu erkennen, sondern verbessert auch Ihre Handelsstrategie durch automatisierte Visualisierung und Ausführung. In diesem Artikel werden wir uns mit folgenden Themen beschäftigen:

  1. Blaupause der Strategie
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende werden Sie einen voll funktionsfähigen Expert Advisor haben, der in der Lage ist, harmonische Schmetterlingsmuster zu erkennen und zu handeln. Fangen wir an!


Blaupause der Strategie

Das Schmetterlingsmuster ist eine präzise geometrische Formation, die durch fünf wichtige Swing- oder Umkehrpunkt - X, A, B, C und D - definiert wird und in zwei Haupttypen auftritt: ein Auf- und ein Abwärtsmuster. Bei einem Abwärts-Schmetterling bildet die Struktur eine Hoch-Tief-Hoch-Tief-Hoch-Sequenz, bei der Pivot X ein Swing-Hoch, Pivot A ein Swing-Tief, Pivot B ein Swing-Hoch, Pivot C ein Swing-Tief und Pivot D ein Swing-Hoch ist (wobei D über X liegt). Umgekehrt bildet sich ein Aufwärts-Schmetterling in einer Tief-Hoch-Tief-Hoch-Tief-Sequenz, wobei der Pivot X ein Swing-Tief darstellt und der Pivot D unter X fällt.

Das harmonischen Muster des Abwärts-Schmetterlings:

ABWÄRTS

Das harmonischen Muster des Aufwärts-Schmetterlings:

AUFWÄRTS

Um die Muster zu identifizieren, werden wir im Folgenden strukturiert vorgehen:

  • Definition des „XA“-Schenkels: Die anfängliche Bewegung vom Umkehrpunkt X nach A legt unsere Referenzdistanz für das Muster fest.
  • Einrichtung des „AB“-Schenkels: Bei beiden Mustern sollte der Umkehrpunkt B idealerweise bei einem 78,6 %igen Retracement der XA-Bewegung liegen, um zu bestätigen, dass der Kurs einen erheblichen Teil der ursprünglichen Bewegung rückgängig gemacht hat.
  • Analyse des „BC“-Schenkels: Dieser Schenkel dürfte zwischen 38,2 % und 88,6 % der XA-Distanz zurückgehen, um eine stabile Konsolidierung vor der endgültigen Bewegung zu gewährleisten.
  • Einstellung des „CD“-Schenkels: Der letzte Schenkel sollte sich zwischen 127 % und 161,8 % der XA-Bewegung erstrecken, was das Muster vervollständigen und einen Umkehrpunkt anzeigen würde.

Durch die Anwendung dieser geometrischen und Fibonacci-basierten Kriterien wird unser Expert Advisor systematisch gültige Schmetterlingsmuster in historischen Kursdaten erkennen. Sobald ein Muster bestätigt wird, visualisiert das Programm die Formation auf dem Chart mit kommentierten Dreiecken und Trendlinien und führt dann Handelsgeschäfte basierend auf den berechneten Einstiegs-, Stop-Loss- und Take-Profit-Levels aus.


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen Sie den MetaEditor, gehen Sie zum Navigator, suchen Sie den Ordner Indikatoren, klicken Sie auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald sie erstellt ist, müssen wir in der Programmierumgebung einige globale Variablen deklarieren, die wir im gesamten Programm verwenden werden.

//+------------------------------------------------------------------+
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "https://t.me/Forex_Algo_Trader" |
//+------------------------------------------------------------------+
#property copyright "Forex Algo-Trader, Allan"
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property description "This EA trades based on Butterfly Strategy"
#property strict

//--- Include the trading library for order functions  
#include <Trade\Trade.mqh>  //--- Include Trade library
CTrade obj_Trade;  //--- Instantiate a obj_Trade object

//--- Input parameters for user configuration  
input int    PivotLeft    = 5;      //--- Number of bars to the left for pivot check  
input int    PivotRight   = 5;      //--- Number of bars to the right for pivot check  
input double Tolerance    = 0.10;   //--- Allowed deviation (10% of XA move)  
input double LotSize      = 0.01;   //--- Lot size for new orders  
input bool   AllowTrading = true;   //--- Enable or disable trading

//---------------------------------------------------------------------------  
//--- Butterfly pattern definition:  
//  
//--- Bullish Butterfly:  
//---   Pivots (X-A-B-C-D): X swing high, A swing low, B swing high, C swing low, D swing high.  
//---   Normally XA > 0; Ideal B = A + 0.786*(X-A); Legs within specified ranges.  
//  
//--- Bearish Butterfly:  
//---   Pivots (X-A-B-C-D): X swing low, A swing high, B swing low, C swing high, D swing low.  
//---   Normally XA > 0; Ideal B = A - 0.786*(A-X); Legs within specified ranges.  
//---------------------------------------------------------------------------

//--- Structure for a pivot point  
struct Pivot {  
   datetime time;   //--- Bar time of the pivot  
   double   price;  //--- Pivot price (High for swing high, low for swing low)  
   bool     isHigh; //--- True if swing high; false if swing low  
};  

//--- Global dynamic array for storing pivots in chronological order  
Pivot pivots[];  //--- Declare a dynamic array to hold identified pivot points

//--- Global variables to lock in a pattern (avoid trading on repaint)  
int      g_patternFormationBar = -1;  //--- Bar index where the pattern was formed (-1 means none)  
datetime g_lockedPatternX      = 0;   //--- The key X pivot time for the locked pattern

Hier binden wir die Bibliothek „Trade\Trade.mqh“ ein, um auf Handelsfunktionen zuzugreifen und das Objekt „obj_Trade“ für die Auftragsausführung zu instanziieren. Wir definieren die Eingabeparameter wie „PivotLeft“ und „PivotRight“ für die Identifizierung der Umkehrpunkte, „Tolerance“ für die Validierung des harmonischen Verhältnisses, „LotSize“ für das Handelsvolumen und „AllowTrading“ zum Aktivieren oder Deaktivieren von Handelsgeschäften.

Um die Marktstruktur zu verfolgen, verwenden wir eine Struktur „Pivot“, die durch eine Struktur definiert ist, die „time“, „price“ und „isHigh“ speichert (true für hohe und false für tiefe Umkehrpunkte). Diese Umkehrpunkte werden in einem globalen, dynamischen Array, „pivots[]“, als historische Referenz gespeichert. Schließlich definieren wir die globalen Variablen „g_patternFormationBar“ und „g_lockedPatternX“, um doppelte Abschlüsse zu verhindern, indem wir ein erkanntes Muster einschließen. Als Nächstes können wir Funktionen definieren, die uns helfen, die Muster im Chart zu visualisieren.

//+------------------------------------------------------------------+  
//| Helper: Draw a filled triangle                                   |  
//+------------------------------------------------------------------+  
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {  
   //--- Attempt to create a triangle object with three coordinate points  
   if(ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) {  
      //--- Set the triangle's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the triangle's line style to solid  
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);  
      //--- Set the line width of the triangle  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);  
      //--- Determine if the triangle should be filled  
      ObjectSetInteger(0, name, OBJPROP_FILL, fill);  
      //--- Set whether the object is drawn in the background  
      ObjectSetInteger(0, name, OBJPROP_BACK, back);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw a trend line                                        |  
//+------------------------------------------------------------------+  
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {  
   //--- Create a trend line object connecting two points  
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) {  
      //--- Set the trend line's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the trend line's style (solid, dotted, etc.)  
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);  
      //--- Set the width of the trend line  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw a dotted trend line                                 |  
//+------------------------------------------------------------------+  
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {  
   //--- Create a horizontal trend line at a fixed price level with dotted style  
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) {  
      //--- Set the dotted line's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);  
      //--- Set the line style to dotted  
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);  
      //--- Set the line width to 1  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw anchored text label (for pivots)                    |  
//| If isHigh is true, anchor at the bottom (label appears above);   |  
//| if false, anchor at the top (label appears below).               |  
//+------------------------------------------------------------------+  
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {  
   //--- Create a text label object at the specified time and price  
   if(ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) {  
      //--- Set the text of the label  
      ObjectSetString(0, name, OBJPROP_TEXT, text);  
      //--- Set the color of the text  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the font size for the text  
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize);  
      //--- Set the font type and style  
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold");  
      //--- Anchor the text depending on whether it's a swing high or low  
      if(isHigh)  
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);  
      else  
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP);  
      //--- Center-align the text  
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER);  
   }  
}  

Wir definieren eine Reihe von Hilfsfunktionen zur Visualisierung von Preisaktionsstrukturen durch das Zeichnen von Dreiecken, Trendlinien, gepunkteten Linien und Textbeschriftungen im Chart. Diese Funktionen helfen bei der Markierung von Schlüsselpunkten, Trendrichtungen und potenziellen Umkehr-Levels. Die Funktion „DrawTriangle“ erstellt ein Dreiecksobjekt, das drei Preispunkte verbindet. Zunächst wird das Objekt vom Typ OBJ_TRIANGLE mit der Funktion ObjectCreate definiert, dann werden die Eigenschaften Farbe, Breite und Füllung mit der Funktion ObjectSetInteger zugewiesen. Diese Funktion ist nützlich, um harmonische Formationen und Preisaktionsmuster zu markieren.

Die Funktion „DrawTrendLine“ zeichnet Trendlinien zwischen zwei Kurspunkten und hilft, die Struktur des Musters zu definieren. Sie erstellt eine Trendlinie mit der Funktion ObjectCreate vom Typ OBJ_TREND und passt dann deren Farbe, Breite und Stil an. Die Funktion „DrawDottedLine“ hilft, eine horizontale gepunktete Linie auf einem bestimmten Kursniveau zwischen zwei Zeitpunkten zu zeichnen. Dies ist nützlich, um Einstiegs- und Ausstiegslevel zu markieren und sicherzustellen, dass wichtige Preiszonen visuell hervorgehoben werden. Die Funktion setzt den Linienstil zur Differenzierung auf STYLE_DOT. Die Funktion „DrawTextEx“ platziert Textbeschriftungen an bestimmten Umkehrpunkten. Es weist der Kennzeichnung einen Namen zu, legt seine Farbe, Schriftgröße und Ausrichtung fest und positioniert es entweder über oder unter dem Preisniveau, je nachdem, ob es sich um ein Swing-High oder Swing-Low handelt. Dies trägt dazu bei, wichtige Umkehr-Level für eine bessere Mustererkennung zu vermerken.

Mit diesen Variablen und Funktionen können wir zu OnTick übergehen und mit der Mustererkennung beginnen. Da wir jedoch nicht bei jedem Tick etwas verarbeiten müssen, müssen wir eine Logik definieren, mit der wir die Identifizierung einmal pro Balken verarbeiten können.

//+------------------------------------------------------------------+  
//| Expert tick function                                             |  
//+------------------------------------------------------------------+  
void OnTick() {  
   //--- Declare a static variable to store the time of the last processed bar  
   static datetime lastBarTime = 0;  
   //--- Get the time of the current confirmed bar  
   datetime currentBarTime = iTime(_Symbol, _Period, 1);  
   //--- If the current bar time is the same as the last processed, exit  
   if(currentBarTime == lastBarTime)  
      return;  
   //--- Update the last processed bar time  
   lastBarTime = currentBarTime;  

}

Um sicherzustellen, dass das Programm die Logik nur bei neuen Balken ausführt, um redundante Berechnungen zu vermeiden, verwenden wir die statische Variable „lastBarTime“, um den Zeitstempel des zuletzt verarbeiteten Balkens zu speichern. Für jeden Tick wird die Zeit des letzten bestätigten Balkens mit der Funktion iTime abgerufen. Wenn die abgefragte Zeit mit „lastBarTime“ übereinstimmt, beenden wir das Programm vorzeitig mit return, um eine erneute Verarbeitung zu vermeiden. Andernfalls aktualisieren wir „lastBarTime“, um den neuen Balken als verarbeitet zu kennzeichnen, und wir können fortfahren, das Speicherarray für die Aufnahme der Daten zur Verarbeitung vorzubereiten.

//--- Clear the pivot array for fresh analysis  
ArrayResize(pivots, 0);  
//--- Get the total number of bars available on the chart  
int barsCount = Bars(_Symbol, _Period);  
//--- Define the starting index for pivot detection (ensuring enough left bars)  
int start = PivotLeft;  
//--- Define the ending index for pivot detection (ensuring enough right bars)  
int end = barsCount - PivotRight;  

//--- Loop through bars from 'end-1' down to 'start' to find pivot points  
for(int i = end - 1; i >= start; i--) {  
   //--- Assume current bar is both a potential swing high and swing low  
   bool isPivotHigh = true;  
   bool isPivotLow = true;  
   //--- Get the high and low of the current bar  
   double currentHigh = iHigh(_Symbol, _Period, i);  
   double currentLow = iLow(_Symbol, _Period, i);  
   //--- Loop through the window of bars around the current bar  
   for(int j = i - PivotLeft; j <= i + PivotRight; j++) {  
      //--- Skip if the index is out of bounds  
      if(j < 0 || j >= barsCount)  
         continue;  
      //--- Skip comparing the bar with itself  
      if(j == i)  
         continue;  
      //--- If any bar in the window has a higher high, it's not a swing high  
      if(iHigh(_Symbol, _Period, j) > currentHigh)  
         isPivotHigh = false;  
      //--- If any bar in the window has a lower low, it's not a swing low  
      if(iLow(_Symbol, _Period, j) < currentLow)  
         isPivotLow = false;  
   }  
   //--- If the current bar qualifies as either a swing high or swing low  
   if(isPivotHigh || isPivotLow) {  
      //--- Create a new pivot structure  
      Pivot p;  
      //--- Set the pivot's time  
      p.time = iTime(_Symbol, _Period, i);  
      //--- Set the pivot's price depending on whether it is a high or low  
      p.price = isPivotHigh ? currentHigh : currentLow;  
      //--- Set the pivot type (true for swing high, false for swing low)  
      p.isHigh = isPivotHigh;  
      //--- Get the current size of the pivots array  
      int size = ArraySize(pivots);  
      //--- Increase the size of the pivots array by one  
      ArrayResize(pivots, size + 1);  
      //--- Add the new pivot to the array  
      pivots[size] = p;  
   }  
}  

Hier identifizieren wir die Umkehrpunkte für das Hoch und das Tief auf dem Chart, indem wir die historischen Kursdaten analysieren. Zunächst setzen wir das Array „pivots“ der Umkehrpunkte mit der Funktion ArrayResize zurück, um eine neue Analyse zu gewährleisten. Anschließend wird mit der Funktion Bars die Gesamtzahl der Balken ermittelt und der Bereich für die Erkennung der Umkehrpunkte festgelegt, damit genügend linke und rechte Balken zum Vergleich zur Verfügung stehen.

Als Nächstes verwenden wir eine for-Schleife, um die Balken von „end-1“ bis „start“ zu durchlaufen, wobei wir davon ausgehen, dass jeder Balken ein potenzieller Umkehrpunkt sein könnte. Mit den Funktionen iHigh und iLow holen wir uns den Höchst- und Tiefstwert des Balkens. Wir vergleichen dann den aktuellen Balken mit den umliegenden Balken innerhalb des Bereichs „PivotLeft“ und „PivotRight“. Wenn ein Balken in diesem Bereich ein höheres Hoch hat, ist der aktuelle Balken kein Swing-Hoch; wenn ein Balken ein niedrigeres Tief hat, ist er kein Swing-Tief. Wenn ein Balken als Umkehrpunkt (pivot) qualifiziert ist, erstellen wir die Struktur „Pivot“, speichern seine Zeit mit der Funktion iTime, setzen seinen Preis je nachdem, ob es sich um ein Hoch oder ein Tief handelt, und bestimmen seinen Typ (true für swing high, false für swing low). Schließlich wird die Größe des Arrays „pivots“ mit ArrayResize angepasst und der identifizierte Pivot hinzugefügt. Wenn wir diese Daten mit der Funktion ArrayPrint drucken, erhalten wir das folgende Ergebnis.

GESPEICHERTE DATEN IM ARRAY

Aus den Daten können wir die Umkehrpunkte extrahieren, und wenn wir genügend Umkehrpunkte haben, können wir die Muster analysieren und erkennen. Hier ist die Logik, mit der wir das erreichen.

//--- Determine the total number of pivots found  
int pivotCount = ArraySize(pivots);  
//--- If fewer than five pivots are found, the pattern cannot be formed  
if(pivotCount < 5) {  
   //--- Reset pattern lock variables  
   g_patternFormationBar = -1;  
   g_lockedPatternX = 0;  
   //--- Exit the OnTick function  
   return;  
}  

//--- Extract the last five pivots as X, A, B, C, and D  
Pivot X = pivots[pivotCount - 5];  
Pivot A = pivots[pivotCount - 4];  
Pivot B = pivots[pivotCount - 3];  
Pivot C = pivots[pivotCount - 2];  
Pivot D = pivots[pivotCount - 1];  

//--- Initialize a flag to indicate if a valid Butterfly pattern is found  
bool patternFound = false;  
//--- Check for the high-low-high-low-high (Bearish reversal) structure  
if(X.isHigh && (!A.isHigh) && B.isHigh && (!C.isHigh) && D.isHigh) {  
   //--- Calculate the difference between pivot X and A  
   double diff = X.price - A.price;  
   //--- Ensure the difference is positive  
   if(diff > 0) {  
      //--- Calculate the ideal position for pivot B based on Fibonacci ratio  
      double idealB = A.price + 0.786 * diff;  
      //--- Check if actual B is within tolerance of the ideal position  
      if(MathAbs(B.price - idealB) <= Tolerance * diff) {  
         //--- Calculate the BC leg length  
         double BC = B.price - C.price;  
         //--- Verify that BC is within the acceptable Fibonacci range  
         if((BC >= 0.382 * diff) && (BC <= 0.886 * diff)) {  
            //--- Calculate the CD leg length  
            double CD = D.price - C.price;  
            //--- Verify that CD is within the acceptable Fibonacci range and that D is above X  
            if((CD >= 1.27 * diff) && (CD <= 1.618 * diff) && (D.price > X.price))  
               patternFound = true;  
         }  
      }  
   }  
}  

Hier überprüfen wir, ob ein harmonisches Schmetterlingsmuster vorliegt, indem wir die letzten fünf identifizierten Umkehrpunkte analysieren. Zunächst wird mit der Funktion ArraySize die Gesamtzahl der Umkehrpunkte bestimmt. Wenn weniger als fünf Umkehrpunkte vorhanden sind, setzen wir die Mustersperrvariablen (“g_patternFormationBar“ und „g_lockedPatternX“) zurück und verlassen die Funktion OnTick, um falsche Signale zu vermeiden. Als Nächstes extrahieren wir die letzten fünf Umkehrpunkte und ordnen sie als „X“, „A“, „B“, „C“ und „D“ zu, wobei wir der geometrischen Struktur des Musters folgen. Dann initialisieren wir das Flag „patternFound“ mit false, um festzustellen, ob die Bedingungen für ein gültiges Schmetterlingsmuster erfüllt sind.

Für ein Abwärts-Umkehrmuster überprüfen wir die Abfolge der Umkehr-Hochs und -Tiefs: „X“ (hoch), „A“ (niedrig), „B“ (hoch), „C“ (niedrig) und „D“ (hoch). Wenn diese Struktur stimmt, berechnen wir die „XA“-Schenkeldifferenz und verwenden Fibonacci-Verhältnisse, um die erwarteten Positionen von „B“, „C“ und „D“ zu überprüfen. Der „B“-Pivot muss in der Nähe des „0,786“-Retracements von „XA“ liegen, „BC“ sollte zwischen „0,382“ und „0.886“ von „XA“ liegen, und „CD“ sollte sich zwischen „1,27“ und „1,618“ von „XA“ erstrecken, um sicherzustellen, dass „D“ über „X“ liegt. Wenn alle diese Bedingungen erfüllt sind, bestätigen wir das Muster, indem wir „patternFound“ auf true setzen. Ähnlich verfahren wir bei einem Aufwärts-Muster.

//--- Check for the low-high-low-high-low (Bullish reversal) structure  
if((!X.isHigh) && A.isHigh && (!B.isHigh) && C.isHigh && (!D.isHigh)) {  
   //--- Calculate the difference between pivot A and X  
   double diff = A.price - X.price;  
   //--- Ensure the difference is positive  
   if(diff > 0) {  
      //--- Calculate the ideal position for pivot B based on Fibonacci ratio  
      double idealB = A.price - 0.786 * diff;  
      //--- Check if actual B is within tolerance of the ideal position  
      if(MathAbs(B.price - idealB) <= Tolerance * diff) {  
         //--- Calculate the BC leg length  
         double BC = C.price - B.price;  
         //--- Verify that BC is within the acceptable Fibonacci range  
         if((BC >= 0.382 * diff) && (BC <= 0.886 * diff)) {  
            //--- Calculate the CD leg length  
            double CD = C.price - D.price;  
            //--- Verify that CD is within the acceptable Fibonacci range and that D is below X  
            if((CD >= 1.27 * diff) && (CD <= 1.618 * diff) && (D.price < X.price))  
               patternFound = true;  
         }  
      }  
   }  
}  

Wenn das Muster gefunden wurde, können wir es im Chart visualisieren.

//--- Initialize a string to store the type of pattern detected  
string patternType = "";  
//--- If a valid pattern is found, determine its type based on the relationship between D and X  
if(patternFound) {  
   if(D.price > X.price)  
      patternType = "Bearish";  //--- Bearish Butterfly indicates a SELL signal  
   else if(D.price < X.price)  
      patternType = "Bullish";  //--- Bullish Butterfly indicates a BUY signal  
}  

//--- If a valid Butterfly pattern is detected  
if(patternFound) {  
   //--- Print a message indicating the pattern type and detection time  
   Print(patternType, " Butterfly pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS));  
   
   //--- Create a unique prefix for all graphical objects related to this pattern  
   string signalPrefix = "BF_" + IntegerToString(X.time);  
   
   //--- Choose triangle color based on the pattern type  
   color triangleColor = (patternType=="Bullish") ? clrBlue : clrRed;  
   
   //--- Draw the first triangle connecting pivots X, A, and B  
   DrawTriangle(signalPrefix+"_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price,  
                triangleColor, 2, true, true);  
   //--- Draw the second triangle connecting pivots B, C, and D  
   DrawTriangle(signalPrefix+"_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price,  
                triangleColor, 2, true, true);  
   
   //--- Draw boundary trend lines connecting the pivots for clarity  
   DrawTrendLine(signalPrefix+"_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID);  
   DrawTrendLine(signalPrefix+"_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID);  
   DrawTrendLine(signalPrefix+"_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID);  
   DrawTrendLine(signalPrefix+"_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID);  
   DrawTrendLine(signalPrefix+"_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID);  
   DrawTrendLine(signalPrefix+"_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID);  
}

Hier schließen wir die Erkennung des Schmetterlingsmusters ab, indem wir es entweder als Auf- oder Abwärts-Muster klassifizieren und visuell auf dem Chart markieren. Zunächst wird die Zeichenkette „patternType“ initialisiert, um zu speichern, ob das erkannte Muster Auf- oder Abwärts ist. Wenn „patternFound“ wahr ist, vergleichen wir Umkehrpunkt „D“ mit dem „X“ unter Verwendung der Eigenschaft „price“. Wenn „D“ höher als „X“ ist, stufen wir es als Abwärts-Muster ein, was eine potenzielle Verkaufsmöglichkeit signalisiert. Umgekehrt, wenn „D“ niedriger als „X“ ist, stufen wir es als Aufwärts-Muster ein, was eine potenzielle Kaufgelegenheit signalisiert.

Sobald ein Muster erkannt wird, wird mit der Funktion Print eine Meldung ausgedruckt, um den Mustertyp und die Erkennungszeit zu protokollieren. Mit der Funktion IntegerToString und „X.time“ wird ein eindeutiger „signalPrefix“ erzeugt, um sicherzustellen, dass jedes Muster eindeutige grafische Objekte hat. Anschließend verwenden wir die Funktion „DrawTriangle“, um die beiden dreieckigen Abschnitte zu markieren, die das Schmetterlingsmuster bilden. Die Dreiecke sind bei Aufwärts-Mustern clrBlue und bei Abwärts-Mustern „clrRed“ eingefärbt. Das erste Dreieck verbindet die Umkehrpunkte „X“, „A“ und „B“, während das zweite Dreieck die Umkehrpunkte „B“, „C“ und „D“ verbindet.

Um die Visualisierung weiter zu verbessern, verwenden wir die Funktion „DrawTrendLine“, um solide schwarze Trendlinien zu erstellen, die die wichtigsten Umkehrpunkte verbinden: „XA“, „AB“, „BC“, „CD“, „XB“ und „BD“. Diese Linien bieten eine klare Struktur zur Identifizierung des harmonischen Musters und seiner Symmetrie. Nach dem Kompilieren und Ausführen erhalten wir folgende Ergebnisse.

MUSTER AUF DEM CHART GEZEICHNET

Anhand des Bildes können wir sehen, dass wir das Muster sowohl identifizieren als auch visualisieren können. Dann können wir mit der Beschriftung fortfahren, um ihre visuelle Klarheit zu verbessern.

//--- Retrieve the symbol's point size to calculate offsets for text positioning  
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);  
//--- Calculate an offset (15 points) for positioning text above or below pivots  
double offset = 15 * point;  

//--- Determine the Y coordinate for each pivot label based on its type  
double textY_X = (X.isHigh ? X.price + offset : X.price - offset);  
double textY_A = (A.isHigh ? A.price + offset : A.price - offset);  
double textY_B = (B.isHigh ? B.price + offset : B.price - offset);  
double textY_C = (C.isHigh ? C.price + offset : C.price - offset);  
double textY_D = (D.isHigh ? D.price + offset : D.price - offset);  

//--- Draw text labels for each pivot with appropriate anchoring  
DrawTextEx(signalPrefix+"_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh);  
DrawTextEx(signalPrefix+"_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh);  
DrawTextEx(signalPrefix+"_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh);  
DrawTextEx(signalPrefix+"_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh);  
DrawTextEx(signalPrefix+"_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh);  

//--- Calculate the central label's time as the midpoint between pivots X and B  
datetime centralTime = (X.time + B.time) / 2;  
//--- Set the central label's price at pivot D's price  
double centralPrice = D.price;  
//--- Create the central text label indicating the pattern type  
if(ObjectCreate(0, signalPrefix+"_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) {  
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_TEXT,  
      (patternType=="Bullish") ? "Bullish Butterfly" : "Bearish Butterfly");  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_COLOR, clrBlack);  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_FONTSIZE, 11);  
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_FONT, "Arial Bold");  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER);  
}  

Hier fügen wir Textbeschriftungen hinzu, um das Schmetterlingsmuster auf dem Chart zu markieren. Zunächst verwenden wir die Funktion SymbolInfoDouble, um den Wert von SYMBOL_POINT des Symbols zu ermitteln und einen Versatz für die Textpositionierung zu berechnen. Die Beschriftungen für die Umkehrpunkte („X“, „A“, „B“, „C“, „D“) werden oben oder unten positioniert, je nachdem, ob es sich um Höchst- oder Tiefststände handelt. Wir verwenden die Funktion „DrawTextEx“, um diese Beschriftungen mit schwarzer Schriftfarbe und Größe 11 zu platzieren. Eine zentrale Kennzeichnung durch die Aufschrift „Bullish Butterfly“ oder „Bearish Butterfly“ wird in der Mitte zwischen „X“ und „B“ erstellt, wobei ObjectCreate, ObjectSetString und ObjectSetInteger verwendet werden, um Text, Farbe, Schriftgröße und Ausrichtung für eine klare Sichtbarkeit festzulegen. Dies ist das Ergebnis nach der Ausführung des Programms.

MUSTER MIT KENNZEICHNUNG

Da wir nun die Beschriftungen haben, können wir nun die Einstiegs- und Ausstiegsebenen hinzufügen.

//--- Define start and end times for drawing horizontal dotted lines for obj_Trade levels  
datetime lineStart = D.time;  
datetime lineEnd = D.time + PeriodSeconds(_Period)*2;  

//--- Declare variables for entry price and take profit levels  
double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff;  
//--- Calculate obj_Trade levels based on whether the pattern is Bullish or Bearish  
if(patternType=="Bullish") { //--- Bullish → BUY signal  
   //--- Use the current ASK price as the entry  
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK);  
   //--- Set TP3 at pivot C's price  
   TP3Level = C.price;  
   //--- Calculate the total distance to be covered by the obj_Trade  
   tradeDiff = TP3Level - entryPriceLevel;  
   //--- Set TP1 at one-third of the total move  
   TP1Level = entryPriceLevel + tradeDiff/3;  
   //--- Set TP2 at two-thirds of the total move  
   TP2Level = entryPriceLevel + 2*tradeDiff/3;  
} else { //--- Bearish → SELL signal  
   //--- Use the current BID price as the entry  
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID);  
   //--- Set TP3 at pivot C's price  
   TP3Level = C.price;  
   //--- Calculate the total distance to be covered by the obj_Trade  
   tradeDiff = entryPriceLevel - TP3Level;  
   //--- Set TP1 at one-third of the total move  
   TP1Level = entryPriceLevel - tradeDiff/3;  
   //--- Set TP2 at two-thirds of the total move  
   TP2Level = entryPriceLevel - 2*tradeDiff/3;  
}  

//--- Draw dotted horizontal lines to represent the entry and TP levels  
DrawDottedLine(signalPrefix+"_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);  
DrawDottedLine(signalPrefix+"_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);  
DrawDottedLine(signalPrefix+"_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);  
DrawDottedLine(signalPrefix+"_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);  

//--- Define a label time coordinate positioned just to the right of the dotted lines  
datetime labelTime = lineEnd + PeriodSeconds(_Period)/2;  

//--- Construct the entry label text with the price  
string entryLabel = (patternType=="Bullish") ? "BUY (" : "SELL (";  
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";  
//--- Draw the entry label on the chart  
DrawTextEx(signalPrefix+"_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true);  

//--- Construct and draw the TP1 label  
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true);  

//--- Construct and draw the TP2 label  
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);  

//--- Construct and draw the TP3 label  
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true);  

Hier berechnen wir den Handelseinstieg und den Take Profit (TP) auf der Grundlage des erkannten Musters. Wir beginnen mit der Funktion PeriodSeconds, um die Dauer für das Zeichnen der horizontalen Handelsstufen zu bestimmen. Anschließend verwenden wir die Funktion SymbolInfoDouble, um den Einstiegskurs zu ermitteln, wobei wir SYMBOL_ASK für einen Kauf und SYMBOL_BID für einen Verkauf anwenden. Wir setzen TP3 über die Variable „C.price“ und berechnen die gesamte Handelsspanne. Wir berechnen TP1 und TP2, indem wir diesen Bereich in Drittel teilen. Wir verwenden die Funktion „DrawDottedLine“, um die Einstiegs- und Endkurse mit unterschiedlichen Farben zu zeichnen. Als Nächstes bestimmen wir mit der Funktion PeriodSeconds eine geeignete Zeitkoordinate für die Kennzeichnung, um es besser zu positionieren. Wir konstruieren die Eintragsbezeichnung mit der Funktion DoubleToString, um den Preis genau zu formatieren. Schließlich wenden wir die Funktion „DrawTextEx“ an, um die Kennzeichnung der Eröffnung und des TP auf dem Chart anzuzeigen. Nach der Kompilierung erhalten wir folgendes Ergebnis.

Abwärts-Muster:

VOLLSTÄNDIGES ABWÄRTS-MUSTER

Aufwärts-Muster:

VOLLSTÄNDIGES AUFWÄRTS-MUSTER

Anhand der Bilder können wir sehen, dass wir beide Muster erkennen und korrekt darstellen können. Wenn das Muster immer noch existiert, bedeutet dies, dass es sich nicht wiederholt hat, sodass wir fortfahren können, die entsprechenden Positionen von der Einstiegsebene aus zu eröffnen. Hier ist die Logik, mit der wir das erreichen.

//--- Retrieve the index of the current bar  
int currentBarIndex = Bars(_Symbol, _Period) - 1;  
//--- If no pattern has been previously locked, lock the current pattern formation  
if(g_patternFormationBar == -1) {  
   g_patternFormationBar = currentBarIndex;  
   g_lockedPatternX = X.time;  
   //--- Print a message that the pattern is detected and waiting for confirmation  
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar.");  
   return;  
}  
//--- If still on the same formation bar, the pattern is considered to be repainting  
if(currentBarIndex == g_patternFormationBar) {  
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No obj_Trade yet.");  
   return;  
}  
//--- If we are on a new bar compared to the locked formation  
if(currentBarIndex > g_patternFormationBar) {  
   //--- Check if the locked pattern still corresponds to the same X pivot  
   if(g_lockedPatternX == X.time) {  
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening obj_Trade on bar ", currentBarIndex, ".");  
      //--- Update the pattern formation bar to the current bar  
      g_patternFormationBar = currentBarIndex;  
      //--- Only proceed with trading if allowed and if there is no existing position  
      if(AllowTrading && !PositionSelect(_Symbol)) {  
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0;  
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);  
         bool tradeResult = false;  
         //--- For a Bullish pattern, execute a BUY obj_Trade  
         if(patternType=="Bullish") {  //--- BUY signal  
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK);  
            double diffTrade = TP2Level - entryPriceTrade;  
            stopLoss = entryPriceTrade - diffTrade * 3;  
            takeProfit = TP2Level;  
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Butterfly Signal");  
            if(tradeResult)  
               Print("Buy order opened successfully.");  
            else  
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription());  
         }  
         //--- For a Bearish pattern, execute a SELL obj_Trade  
         else if(patternType=="Bearish") {  //--- SELL signal  
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID);  
            double diffTrade = entryPriceTrade - TP2Level;  
            stopLoss = entryPriceTrade + diffTrade * 3;  
            takeProfit = TP2Level;  
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Butterfly Signal");  
            if(tradeResult)  
               Print("Sell order opened successfully.");  
            else  
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription());  
         }  
      }  
      else {  
         //--- If a position is already open, do not execute a new obj_Trade  
         Print("A position is already open for ", _Symbol, ". No new obj_Trade executed.");  
      }  
   }  
   else {  
      //--- If the pattern has changed, update the lock with the new formation bar and X pivot  
      g_patternFormationBar = currentBarIndex;  
      g_lockedPatternX = X.time;  
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation.");  
      return;  
   }  
}  
}  
else {  
//--- If no valid Butterfly pattern is detected, reset the pattern lock variables  
g_patternFormationBar = -1;  
g_lockedPatternX = 0;  
}  

Dieser Abschnitt verwaltet das Sperren des Musters und die Handelsausführung. Zunächst wird mit der Funktion Bars der aktuelle Balkenindex ermittelt und „currentBarIndex“ zugewiesen. Wenn kein Muster gesperrt wurde, was durch „g_patternFormationBar“ == -1 angezeigt wird, weisen wir „currentBarIndex“ „g_patternFormationBar“ zu und speichern die Umkehrzeit von X in „g_lockedPatternX“, wobei wir mit der Funktion „Print“ eine Meldung ausgeben, dass ein Muster erkannt wurde und auf eine Bestätigung wartet. Wenn sich das erkannte Muster immer noch auf demselben Balken bildet, verwenden wir die Funktion Print, um eine Meldung anzuzeigen, die darauf hinweist, dass das Muster neu gezeichnet wird, und es wird kein Handel ausgeführt.

Wenn der aktuelle Balken über den gesperrten Formationsbalken hinausgeht, prüfen wir, ob das Muster weiterhin gültig ist, indem wir „g_lockedPatternX“ mit der aktuellen X-Umkehr-Zeit vergleichen. Wenn es übereinstimmt, bestätigen wir das Muster und bereiten die Ausführung des Handels vor. Bevor wir einen Auftrag erteilen, verwenden wir die Funktion PositionSelect, um sicherzustellen, dass keine Position vorhanden ist, und überprüfen „AllowTrading“. Wird ein Aufwärts-Muster bestätigt, ermitteln wir den Briefkurs mit der Funktion SymbolInfoDouble mit SYMBOL_ASK, berechnen den Stop-Loss und den Take-Profit auf Basis von „TP2Level“ und führen einen Kaufauftrag mit der Funktion „obj_Trade.Buy“ aus. Wenn der Handel erfolgreich war, wird mit der Funktion „Print“ eine Bestätigungsmeldung angezeigt; andernfalls wird mit der Funktion „obj_Trade.ResultRetcodeDescription“ der Grund für den Misserfolg gedruckt.

Bei einem Abwärts-Muster wird der Geldkurs mit der Funktion SymbolInfoDouble mit SYMBOL_BID abgefragt, die Handelsstufen berechnet und eine Verkaufsorder mit der Funktion „obj_Trade.Sell“ ausgeführt, wobei entsprechende Erfolgs- oder Misserfolgsmeldungen mit der Funktion Print ausgedruckt werden. Besteht bereits eine Position, wird kein neuer Abschluss getätigt und eine Meldung über die Funktion „Drucken“ ausgedruckt. Wenn sich der gesperrte X-Punkt ändert, werden „g_patternFormationBar“ und „g_lockedPatternX“ aktualisiert, um anzuzeigen, dass sich das Muster geändert hat und eine Bestätigung erwartet wird. Wenn kein gültiges Muster erkannt wird, werden „g_patternFormationBar“ und „g_lockedPatternX“ zurückgesetzt, um vorherige Sperren zu löschen.

Nach der Kompilierung erhalten wir folgendes Ergebnis.

BESTÄTIGTES MUSTER

Aus dem Bild können wir ersehen, dass wir das Schmetterlingsmuster zeichnen und immer noch in der Lage sind, es entsprechend zu handeln, sobald bestätigt wird, dass es stabil ist, und somit unser Ziel zu erreichen, das Muster zu identifizieren, aufzuzeichnen und zu handeln. Bleibt nur noch der Backtest des Programms, und das wird im nächsten Abschnitt behandelt.


Backtest und Optimierung

Nach einem gründlichen Backtest haben wir die folgenden Ergebnisse.

Backtest-Grafik:

GRAPH

Backtest-Bericht:

BERICHT

Der Testzeitraum von einem halben Jahr auf einem 5-Minuten-Chart mit 65 Handelsgeschäfte zeigt, dass das Schmetterlingsmuster selten ist, und je höher der Toleranzprozentsatz, desto höher die Anzahl der Signale.


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir erfolgreich einen MQL5 Expert Advisor (EA) entwickelt haben, der das harmonische Schmetterlingsmuster mit Präzision erkennt und handelt. Durch die Nutzung von Mustererkennung, Umkehr-Validierung und automatischer Handelsausführung haben wir ein System geschaffen, das sich dynamisch an die Marktbedingungen anpasst.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Marktbedingungen können unvorhersehbar sein. Die skizzierte Strategie bietet zwar einen strukturierten Ansatz für den harmonischen Handel, ist aber keine Garantie für Rentabilität. Umfassende Backtests und ein angemessenes Risikomanagement sind unerlässlich, bevor dieses Programm in einer Live-Umgebung eingesetzt wird.

Durch die Anwendung dieser Techniken können Sie Ihre Fähigkeiten im Handel mit harmonischen Mustern verfeinern, Ihre technische Analyse verbessern und Ihre algorithmischen Handelsstrategien weiterentwickeln. Viel Glück auf Ihrer Handelsreise!

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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Trifon Shterev
Trifon Shterev | 22 Feb. 2025 in 22:15
Könnten Sie bitte den gesamten Code zur Verfügung stellen?
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 15): Einführung in die Quarters-Theorie (I) - Quarters Drawer Script Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 15): Einführung in die Quarters-Theorie (I) - Quarters Drawer Script
Unterstützungs- und Widerstandspunkte sind kritische Niveaus, die potenzielle Trendumkehr und -fortsetzungen signalisieren. Obwohl es schwierig sein kann, diese Niveaus zu identifizieren, sind Sie, wenn Sie sie einmal gefunden haben, gut vorbereitet, um sich auf dem Markt zurechtzufinden. Als weitere Hilfe können Sie das in diesem Artikel vorgestellte Tool „Quarters Drawer“ verwenden, mit dem Sie sowohl primäre als auch sekundäre Unterstützungs- und Widerstandsniveaus identifizieren können.
Automatisieren von Handelsstrategien in MQL5 (Teil 7): Aufbau eines Raster-Handel EA mit dynamischer Losgrößen-Skalierung Automatisieren von Handelsstrategien in MQL5 (Teil 7): Aufbau eines Raster-Handel EA mit dynamischer Losgrößen-Skalierung
In diesem Artikel bauen wir einen Expert Advisor in MQL5 für einen Raster-Handel, der eine dynamische Los-Skalierung verwendet. Wir behandeln die Strategieentwicklung, die Code-Implementierung und den Backtest-Prozess. Abschließend vermitteln wir wichtige Erkenntnisse und bewährte Verfahren zur Optimierung des automatisierten Handelssystems.
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (III): Kommunikationsmodul Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (III): Kommunikationsmodul
Nehmen Sie an einer ausführlichen Diskussion über die neuesten Fortschritte im MQL5-Schnittstellendesign teil, wenn wir das neu gestaltete Kommunikations-Panel vorstellen und unsere Serie über den Aufbau des neuen Admin-Panels unter Verwendung von Modularisierungsprinzipien fortsetzen. Wir werden die Klasse CommunicationsDialog Schritt für Schritt entwickeln und ausführlich erklären, wie man sie von der Klasse Dialog erbt. Außerdem werden wir Arrays und die ListView-Klasse in unserer Entwicklung nutzen. Gewinnen Sie umsetzbare Erkenntnisse, um Ihre MQL5-Entwicklungsfähigkeiten zu verbessern - lesen Sie den Artikel und beteiligen Sie sich an der Diskussion im Kommentarbereich!
Ein neuer Ansatz für nutzerdefinierte Kriterien in den Optimierungen (Teil 1): Beispiele für Aktivierungsfunktionen Ein neuer Ansatz für nutzerdefinierte Kriterien in den Optimierungen (Teil 1): Beispiele für Aktivierungsfunktionen
Der erste einer Reihe von Artikeln, die sich mit der Mathematik der nutzerdefinierten Kriterien befassen, mit besonderem Schwerpunkt auf nichtlinearen Funktionen, die in neuronalen Netzen verwendet werden, MQL5-Code für die Implementierung und die Verwendung von gezielten und korrigierenden Offsets.