Auf Fraktalen basiertes Zeichnen von Trendlinien mithilfe von MQL4 und MQL5

Almat Kaldybay | 13 Juni, 2016

Inhaltsverzeichnis

Einleitung

Ich habe mir kürzlich einige Gedanken über die Verwendung von Trendlinien gemacht. Dabei kam die Frage auf, welche Methode man zur Bestimmung der Punkte zum Zeichnen der Linien verwenden soll - sowie die Frage nach der Präzision des Zeichnens. Ich habe mich für Fraktale als Basis entschieden.

Infolge meines Hauptberufs analysiere ich sehr oft Märkte, was es mir erlaubt, ein wenig Zeit ins Trading zu investieren. Ferner gilt, dass man nicht einfach so Linien auf einen größeren Zeitrahmen zeichnen kann - die Linien sollten durch extreme Punkte und mit einer Präzision von bis zu 15 Minuten gezeichnet werden. Die Ursache ist die, dass die Fraktalzeit eines größeren Zeitrahmens nicht immer mit der Zeit des selben Extrempunktes (M15) übereinstimmt. Kurs gesagt: An dieser Stelle kommt die Automatisierung ins Spiel. Es trug sich einmal zu, dass ich einen Code mithilfe von MQL5 geschrieben und dann auf MQL4 gewechselt habe, da ich das Programm für MetaTrader 4 benötigte.

In diesem Artikel werde ich Ihnen meine Lösung des Problems erläutern, indem ich MQL4 und MQL5 verwenden werde. Der Artikel soll zwar beide Sprachen miteinander vergleichen, allerdings wäre es unfair, die Effizienz von MQL4 gegen die von MQL5 antreten zu lassen. Außerdem bin ich mir natürlich darüber im Klaren, dass es wahrscheinlich auch noch andere Lösungen gibt, die eventuell sogar noch effektiver als meine eigene sind. In jedem Fall richtet sich vorliegender Artikel an Anfänger im Schreiben von Skripten mittels MQL5 und MQL4 bzw. an all jene, die planen, mit Fraktalen und Trendlinien zu interagieren.

1. Eingabeparameter, die Funktion DeInit() und die Deklaration von Variablen

Ich habe die folgenden Variablen und Eingabeparameter benutzt:

input color Resistance_Color=Red;       // setting the resistance line color
input ENUM_LINE_STYLE Resistance_Style; // setting the resistance line style
input int Resistance_Width=1;           // setting the resistance line width
input color Support_Color=Red;          // setting the support line color
input ENUM_LINE_STYLE Support_Style;    // setting the support line style
input int Support_Width=1;              // setting the support line width

Diese Parameter sind für MQL4 und MQL5 absolut identisch.

In MQL5 müssen wir den Indikator im Voraus anlegen.

//--- iFractals indicator handle 
int Fractal;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- getting the iFractals indicator handle
   Fractal=iFractals(Symbol(),PERIOD_D1);
//---
   return(INIT_SUCCEEDED);
  }

Da das Programm grafische Objekte zeichnen wird, ergibt es Sinn, diese zu entfernen, wenn man den EA vom Chart abzieht:

void OnDeinit(const int reason)
  {
   ObjectDelete(0,"TL_Resistance");
   ObjectDelete(0,"TL_Support");
  }

Zum Zeichnen zweier Linien (Support und Resistance) bedarf es vier Punkten. Um den Punkt zu ermitteln, den die Linie passiert, müssen wir die Zeit und den Kurs kennen.

Die Koordinaten werden entsprechend folgender Reihenfolge bestimmt: Zuerst müssen wir den Extrembalken finden, woraufhin wir den Kurs und die Zeit des Extrempunktes bestimmen können.

Variablen in der Funktion OnTick() deklarieren.

MQL4
//--- declaration of variables
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;
MQL5
//--- declaration of variables
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;
//--- declaring the arrays for writing values of the iFractal indicator buffer
double FractalDown[],FractalUp[];
double UpFractal_1,UpFractal_2,LowFractal_1,LowFractal_2;

Zunächst habe ich nur diejenigen Variablen deklariert, die Indexe von Balken mit geformten Fraktalen speichern.

In MQL4:

  1. Die Variable n wird dafür gebraucht, um das nächste bekannte Fraktal unter Zuhilfenahme des For-Loop-Operators zu finden;
  2. UpperFractal_1, UpperFractal_2,  LowerFractal_1, LowerFractal_2 - all diese Variablen werden den Index des Balkens am nächsten und zweitnächsten Extrempunkt mit dem höchsten/niedrigsten Preis (bzgl. der Bestimmung des Fraktals) speichern;

In MQL5 führen wir zusätzliche Variablen ein:

  1. FractalDown[], FractalUp[]; - Deklarierung von Arrays des Werts double zum Speichern der Werte des iFractals-Indikatorpuffers;
  2. Als Nächstes folgen die Variablen des Typs double: UpFractal_1, UpFractal_2, LowFractal_1, LowFractal_2. Diese werden den Wert des Preises der Extrempunkte speichern.

2. Die Suche nach den nächstgelegenen Fraktalen

Um den Balkenindex mit einem geformten Fraktal zu finden, verwenden wir den For-Loop-Operator.

Lassen Sie uns die Indexe der beiden ersten Balken bestimmen, die mit dem ersten/zweiten oberen Fraktal korrespondieren:

MQL4
//--- finding the bar index of the first nearest upper fractal
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_1=n+1;
     }
//--- finding the bar index of the second nearest upper fractal
   for(n=UpperFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_2=n+1;
     }
 MQL5
//--- first, we need to write the Fractal indicator buffer values into the arrays
//--- filling arrays with buffer values
   CopyBuffer(Fractal,0,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalUp);
   CopyBuffer(Fractal,1,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalDown);
//--- indexing like in timeseries
   ArraySetAsSeries(FractalUp,true);
   ArraySetAsSeries(FractalDown,true);
//--- next, we use the for loop operator to find the first upper fractal
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- if the value is not empty, break the loop
      if(FractalUp[n]!=EMPTY_VALUE)
         break;
     }
//--- writing the price value of the first fractal into the variable
   UpFractal_1=FractalUp[n];
//--- writing the index of the first fractal into the variable
   UpperFractal_1=n;
//--- finding the second upper fractal 
   for(n=UpperFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalUp[n]!=EMPTY_VALUE) //if the value is not empty, break the loop
         break;
     }
//--- writing the price value of the second fractal into the variable
   UpFractal_2=FractalUp[n];
//--- writing the index of the second fractal into the variable
   UpperFractal_2=n;

Ich habe hier klipp und klar mithilfe von Funktionen zur Bewertung von Zeitserien einen der Hauptunterschiede zwischen MQL5 und MQ4 aufgezeigt.

In MQL4 habe ich umgehend damit begonnen, den Index des Balkens mit einem geformten Fraktal zu suchen. In MQL 5 habe ich hingegen die FractalUp[]-/FractalDown[]-Arrays zum Speichern von Preiswerten von oberen und unteren Fraktalen spezifiziert, indem ich auf den Indikator iFractals unter Zuhilfenahme der Funktion CopyBuffer() zugegriffen habe. Danach habe ich die Indexierung dieser Arrays (ähnlich den Zeitserien) vorgenommen - und zwar mithilfe der Funktion ArraySetAsSeries().

In MQL 4 hatte ich nur Indexe der Balken mit bekannten Fraktalen, aber in MQL 5 erhielt ich durch die Funktion CopyBuffer() Balkenindexe sowie Preiswerte von Fraktalen.

Auf ähnliche Weise finden wir die ersten beiden niedrigeren Fraktale:

MQL4
//--- finding the bar index of the first nearest lower fractal
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_1=n+1;
     }
//--- finding the bar index of the second nearest lower fractal
   for(n=LowerFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_2=n+1;
     }
 MQL5
//--- finding the values of the lower fractals
//--- finding the first lower fractal
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- if the value is not empty, break the loop
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- writing the price value of the first fractal into the variable
   LowFractal_1=FractalDown[n];
//--- writing the index of the first fractal into the variable
   LowerFractal_1=n;
//--- finding the second lower fractal 
   for(n=LowerFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- writing the price value of the second fractal into the variable
   LowFractal_2=FractalDown[n];
//--- writing the index of the second fractal into the variable
   LowerFractal_2=n;

Wie Sie sehen, ist der Code in MQL4 und MQL5 ziemlich ähnlich. Allerdings gibt es einen kleinen Syntaxunterschied.

3. Bestimmung des Kurses und der Zeitwerte der Fraktale

Um eine Linie zu zeichnen, müssen wir zunächst die Zeit und den Preis eines Fraktals bestimmen. Natürlich könnten wir in MQL4 ganz einfach die vordefinierten Zeitserien High[] und Low[] sowie die Funktion iTime() verwenden. Allerdings benötigen wir außerdem noch ein wenig präzisere Zeitkoordinaten, um ein korrektes Einzeichnen der Trendlinie sicherzustellen.

Die Abb. 1-2 zeigen die Differenz zwischen den Zeitwerten der H4-Extrempunkte und der M15-Zeitrahmen an.

Abb. 1 Der Zeitwert des Extrempunktes (H4)

Abb. 1 Der Zeitwert des Extrempunktes (H4)

Abb. 2 Der Zeitwert des Extrempunktes (M15)

Abb. 2 Der Zeitwert des Extrempunktes (M15)

Ich bin zu der Überzeugung gelangt, dass eine Extrempunktpräzision von 15 Minuten für diesen Zweck vollauf genügt.

Grundsätzlich gilt: Das Prinzip zur Klarstellung der Extrempunkte ist in MQL4 und MQL5 beinahe identisch. Gleichwohl gibt es einige Unterschiede hinsichtlich der Details:

MQL4MQL5
  1. Bestimmen Sie den Zeitwert des Extrempunkts für einen größeren Zeitrahmen;
  2. Bestimmen Sie mittels des gefundenen Zeitwerts den Index des Extrembalkens für einen kleineren Zeitrahmen via iBarShift()-Funktion;
  3. Da 24 Stunden als ein Array von 96 15-Minuten-Balken dargestellt werden können, begeben wir uns auf die Suche nach einem Extrempunkt (höchste/niedrigste Wert), der sich unter diesen 96 Elementen befindet. Hierfür verwenden wir die Funktionen iHigh()iLow() , iTime(), ArrayMaximum() und ArrayMinimum().
  1. Bestimmen Sie den Zeitwert des Extrempunkts für einen größeren Zeitrahmen;
  2. Durch den gefundenen Zeitwert können wir nun die Generationszeit des Balkens des nächsten Tages bestimmen. Wir benötigen diesen Wert, um ihn in die folgenden Funktionen einzusetzen: CopyHigh(), CopyLow() und CopyTime().
  3. Deklarieren/füllen Sie das Array (aus), um den Preis bzw. die Zeitwerte für den 15-Minuten-Zeitrahmen zu speichern.
  4. Durch die Verwendung der Funktionen ArrayMaximum() und ArrayMinimum() finden Sie die niedrigsten und höchsten Preis- als auch Zeitwerte der betreffenden Extrempunkte. 

Der Code mit detaillierten Anmerkungen steht unten:

 MQL4
// Step 1. Determining the extreme point time value on a larger timeframe:
//--- determining the time of fractals
   datetime UpFractalTime_1=iTime(NULL, 1440,UpperFractal_1);
   datetime UpFractalTime_2=iTime(NULL, 1440,UpperFractal_2);
   datetime LowFractalTime_1=iTime(NULL, 1440,LowerFractal_1);
   datetime LowFractalTime_2=iTime(NULL, 1440,LowerFractal_2);
// Step 2.  Determining the index of the extreme bar on a smaller timeframe:   
//--- finding the fractal index on M15
   int UpperFractal_1_m15=iBarShift(NULL, 15, UpFractalTime_1,true);
   int UpperFractal_2_m15=iBarShift(NULL, 15, UpFractalTime_2,true);
   int LowerFractal_1_m15=iBarShift(NULL, 15, LowFractalTime_1,true);
   int LowerFractal_2_m15=iBarShift(NULL, 15, LowFractalTime_2,true);
// Step 3. Using the arrays to find the clarified extreme points on М15:
//--- using the arrays to find the clarified extreme points
//--- introducing the i variable to use in the for loop operator
   int i;
//--- 1. First, find the lower extreme points
//--- 3.1 Finding the first lower extreme point
//--- declaring the array for storing the index values of the bars
   int Lower_1_m15[96];
//--- declaring the array for storing the price values
   double LowerPrice_1_m15[96];
//--- starting the for loop:
   for(i=0;i<=95;i++)
     {
      //--- filling the array with the bar index values
      Lower_1_m15[i]=LowerFractal_1_m15-i;
      //--- filling the array with the price values
      LowerPrice_1_m15[i]=iLow(NULL,15,LowerFractal_1_m15-i);
     }
//--- determining the minimum price value in the array
   int LowestPrice_1_m15=ArrayMinimum(LowerPrice_1_m15,WHOLE_ARRAY,0);
//--- determining the bar with the lowest price in the array
   int LowestBar_1_m15=Lower_1_m15[LowestPrice_1_m15];
//--- determining the time of the lowest price bar
   datetime LowestBarTime_1_m15=iTime(NULL,15,Lower_1_m15[LowestPrice_1_m15]);

//--- 3.2 Finding the second lower extreme point
   int Lower_2_m15[96];
   double LowerPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- filling the array with the bar index values
      Lower_2_m15[i]=LowerFractal_2_m15-i;
      //--- filling the array with the price values
      LowerPrice_2_m15[i]=iLow(NULL,15,LowerFractal_2_m15-i);
     }
//--- determining the minimum price value in the array
   int LowestPrice_2_m15=ArrayMinimum(LowerPrice_2_m15,WHOLE_ARRAY,0);
//--- determining the bar with the lowest price in the array
   int LowestBar_2_m15=Lower_2_m15[LowestPrice_2_m15];
//--- determining the time of the lowest price bar
   datetime LowestBarTime_2_m15=iTime(NULL,15,Lower_2_m15[LowestPrice_2_m15]);

//--- 3.3 Finding the first upper extreme point
   int Upper_1_m15[96];
   double UpperPrice_1_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- filling the array with the bar index values
      Upper_1_m15[i]=UpperFractal_1_m15-i;
      //--- filling the array with the price values
      UpperPrice_1_m15[i]=iHigh(NULL,15,UpperFractal_1_m15-i);
     }
//--- determining the maximum price value in the array
   int HighestPrice_1_m15=ArrayMaximum(UpperPrice_1_m15,WHOLE_ARRAY,0);
//--- determining the bar with the highest price in the array
   int HighestBar_1_m15=Upper_1_m15[HighestPrice_1_m15];
//--- determining the time of the highest price bar
   datetime HighestBarTime_1_m15=iTime(NULL,15,Upper_1_m15[HighestPrice_1_m15]);

//--- 3.4 Finding the second upper extreme point
   int Upper_2_m15[96];
   double UpperPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- filling the array with the bar index values
      Upper_2_m15[i]=UpperFractal_2_m15-i;
      //--- filling the array with the price values
      UpperPrice_2_m15[i]=iHigh(NULL,15,UpperFractal_2_m15-i);
     }
 MQL5
// Step 1. Determining the extreme point time value on a larger timeframe:
//--- declaring the arrays for storing the time values of the corresponding bar index on a larger timeframe
   datetime UpFractalTime_1[],LowFractalTime_1[],UpFractalTime_2[],LowFractalTime_2[];
//--- determining the time of fractals on a larger timeframe
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_1,1,UpFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_1,1,LowFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_2,1,UpFractalTime_2);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_2,1,LowFractalTime_2);
// Step 2. Determining the generation time of the next day bar:
//--- determining the generation time of the next day bar (the stop time for CopyHigh(), CopyLow() and CopyTime())
   datetime UpFractalTime_1_15=UpFractalTime_1[0]+86400;
   datetime UpFractalTime_2_15=UpFractalTime_2[0]+86400;
   datetime LowFractalTime_1_15=LowFractalTime_1[0]+86400;
   datetime LowFractalTime_2_15=LowFractalTime_2[0]+86400;
// Step 3. Declaring and filling the arrays for storing the price and time values for the 15-minute timeframe:   
//--- declaring the arrays for storing the maximum and minimum price values
   double High_1_15[],Low_1_15[],High_2_15[],Low_2_15[];
//--- filling the arrays with the CopyHigh() and CopyLow() functions
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15);
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15);
//--- declaring the arrays for storing the time values corresponding to the extreme bar indexes  
   datetime High_1_15_time[],High_2_15_time[],Low_1_15_time[],Low_2_15_time[];
//--- filling the arrays
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15_time);
// Step 4. Finding the lowest and highest price values, and the time values of the clarified extreme points:
//--- determining the highest and lowest price and time values with the ArrayMaximum() and ArrayMinimum() functions
   int Max_M15_1=ArrayMaximum(High_1_15,0,96);
   int Max_M15_2=ArrayMaximum(High_2_15,0,96);
   int Min_M15_1=ArrayMinimum(Low_1_15,0,96);
   int Min_M15_2=ArrayMinimum(Low_2_15,0,96);

Wir haben schließlich die folgenden Trendlinienkoordinaten bestimmt:

1. Für die Support-Linie

MQL4MQL5
  1. Erste Zeitkoordinate - LowestBarTime_2_m15;
  2. Erste Preiskoordinate - LowerPrice_2_m15[LowestPrice_2_m15];
  3. Zweite Zeitkoordinate - LowestBarTime_1_m15;
  4. Zweite Preiskoordinate - LowerPrice_1_m15[LowestPrice_1_m15].
  1. Erste Zeitkoordinate - Low_2_15_time[Min_M15_2];
  2. Erste Preiskoordinate - Low_2_15[Min_M15_2];
  3. Zweite Zeitkoordinate - Low_1_15_time[Min_M15_1];
  4. Zweite Preiskoordinate - Low_1_15[Min_M15_1].

2. Für die Resistance-Linie

MQL4MQL5
  1. Erste Zeitkoordinate - HighestBarTime_2_m15;
  2. Erste Preiskoordinate - UpperPrice_2_m15[HighestPrice_2_m15];
  3. Zweite Zeitkoordinate - HighestBarTime_1_m15;
  4. Zweite Preiskoordinate - UpperPrice_1_m15[HighestPrice_1_m15].
  1. Erste Zeitkoordinate - High_2_15_time[Max_M15_2];
  2. Erste Preiskoordinate - High_2_15[Max_M15_2];
  3. Zweite Zeitkoordinate - High_1_15_time[Max_M15_1];
  4. Zweite Preiskoordinate - High_1_15[Max_M15_1].

4. Das Kreieren von Objekten und Editieren ihrer Eigenschaften Das Neuzeichnen von Linien

Sobald wir die Koordinaten der Linie kennen, müssen wir nur noch die grafischen Objekte generieren:

MQL4
//--- creating the support line
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,LowestBarTime_2_m15,LowerPrice_2_m15[LowestPrice_2_m15],
                LowestBarTime_1_m15,LowerPrice_1_m15[LowestPrice_1_m15]);
   ObjectSet("TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSet("TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSet("TL_Support",OBJPROP_WIDTH,Support_Width);
//--- creating the resistance line
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,HighestBarTime_2_m15,UpperPrice_2_m15[HighestPrice_2_m15],
                HighestBarTime_1_m15,UpperPrice_1_m15[HighestPrice_1_m15]);
   ObjectSet("TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSet("TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSet("TL_Resistance",OBJPROP_WIDTH,Resistance_Width);
MQL5
//--- creating the support line
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,Low_2_15_time[Min_M15_2],Low_2_15[Min_M15_2],Low_1_15_time[Min_M15_1],Low_1_15[Min_M15_1]);
   ObjectSetInteger(0,"TL_Support",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSetInteger(0,"TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSetInteger(0,"TL_Support",OBJPROP_WIDTH,Support_Width);
//--- creating the resistance line
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,High_2_15_time[Max_M15_2],High_2_15[Max_M15_2],High_1_15_time[Max_M15_1],High_1_15[Max_M15_1]);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_WIDTH,Resistance_Width);

Ich habe also die notwendigen Linien eingezeichnet und ihre Parameter auf Basis der Eingabeparameter spezifiziert.

Nun gilt es, die Trendlinien rot zu färben.

Wenn sich die Situation des Marktes verändert - wenn beispielsweise ein neuer Extrempunkt auftaucht - können wir die existierende Linie ganz einfach entfernen.

MQL4
//--- redrawing the support line
//--- writing the values of the support line time coordinates into the variables
   datetime TL_TimeLow2=ObjectGet("TL_Support",OBJPROP_TIME2);
   datetime TL_TimeLow1=ObjectGet("TL_Support",OBJPROP_TIME1);
//--- if the line coordinates don't match the current coordinates
   if(TL_TimeLow2!=LowestBarTime_1_m15 && TL_TimeLow1!=LowestBarTime_2_m15)
     {
      //--- remove the line
      ObjectDelete(0,"TL_Support");
     }
//--- redrawing the resistance line
//--- writing the values of the resistance line time coordinates into the variables
   datetime TL_TimeUp2=ObjectGet("TL_Resistance",OBJPROP_TIME2);
   datetime TL_TimeUp1=ObjectGet("TL_Resistance",OBJPROP_TIME1);
//--- if the line coordinates don't match the current coordinates
   if(TL_TimeUp2!=HighestBarTime_1_m15 && TL_TimeUp1!=HighestBarTime_2_m15)
     {
      //--- remove the line
      ObjectDelete(0,"TL_Resistance");
     }
MQL5
//--- redrawing the support line
//--- writing the values of the support line time coordinates into the variables
   datetime TL_TimeLow2=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,0);
   datetime TL_TimeLow1=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,1);
//--- if the line coordinates don't match the current coordinates
   if(TL_TimeLow2!=Low_2_15_time[Min_M15_2] && TL_TimeLow1!=Low_1_15_time[Min_M15_1])
     {
      //--- remove the line
      ObjectDelete(0,"TL_Support");
     }
//--- redrawing the resistance line
//--- writing the values of the resistance line time coordinates into the variables
   datetime TL_TimeUp2=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,0);
   datetime TL_TimeUp1=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,1);
//--- if the line coordinates don't match the current coordinates
   if(TL_TimeUp2!=High_2_15_time[Max_M15_2] && TL_TimeUp1!=High_1_15_time[Max_M15_1])
     {
      //--- remove the line
      ObjectDelete(0,"TL_Resistance");
     }

5. Überprüfung der Ladehistorie von Balken

Während der Testphase habe ich bemerkt, dass die Linien nicht immer korrekt gezeichnet wurden.

Zuerst nahm ich an, dass es sich ganz einfach um einen Bug im Code handeln würde. Oder das meine Lösung einfach nicht funktioniert. Später begriff ich, dass das Problem durch ein unzureichendes Laden der Balkenhistorie für einen kleineren Zeitraum (bei mir: M15) hervorgerufen wurde. Um andere Benutzer vor diesem Fehler zu bewahren, habe ich mich entschieden, das Programm zusätzlich überprüfen zu lassen, ob ein Balken für M15 existiert.

Zu diesem Zwecke habe ich mir die Fähigkeiten der MQL4-Funktion iBarShift() zunutze gemacht, die ich ursprünglich im Abschnitt „Bestimmung der Preis- und Zeitwerte der Fraktale" verwendet habe.

Falls kein Balken gefunden wird, gibt die Funktion iBarShift() -1 zurück. Wir können daher folgende Warnung ausgeben:

MQL4
//--- checking the bars history loading
//--- if at least one bar is not found on M15
   if(UpperFractal_1_m15==-1 || UpperFractal_2_m15==-1
      || LowerFractal_1_m15==-1 || LowerFractal_2_m15==-1)
     {
      Alert("The loaded history is insufficient for the correct work!");
     }

In MQL5 habe ich die Funktion Bars() verwandt, die einen leeren Wert zurückgibt, wenn die Zeitseriendaten im Terminal nicht generiert worden sein sollten:

 
//--- checking the bars history loading
//--- 1. determining the number of bars on a specified timeframe
   int High_M15_1=Bars(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15);
   int High_M15_2=Bars(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15);
   int Low_M15_1=Bars(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15);
   int Low_M15_2=Bars(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15);
//--- 2. check if the loaded history is insufficient for the correct line drawing
//--- if at least one bar is not found
   if(High_M15_1==0 || High_M15_2==0 || Low_M15_1==0 || Low_M15_2==0)
     {
      Alert("The loaded history is insufficient for the correct work!");
     }

6. Signale von Trendlinien-Breakthroughs, Push Notifications

Um das Puzzle zu vervollständigen, habe ich mich entschieden, ein Signal für Trendlinen-Breakthroughs zu implementieren. Die Trendlinie wird durch die Extrempunkte des Zeitrahmen des Tages gezeichnet. Um allerdings den Breakthrough vorher zu erkennen, muss der Balken höher oder niedriger als die H4-Trendlinie geschlossen werden.

Allgemein gesprochen lässt sich dieser Prozess in drei Schritte untergliedern:

  1. Bestimmung des Schluss- und des Trendlinienpreises des Balkens;
  2. Bestimmung der Bedingungen, die zum Durchbrechen der Trendlinie erfüllt sein müssen;
  3. Versendung einer Push Notification, die den Breakthrough ankündigt.
MQL4
// 1. Getting the price parameters of the trend line 
//--- determining the closing price of a bar with index 1
   double Price_Close_H4=iClose(NULL,240,1);
//--- determining the time of a bar with index 1
   datetime Time_Close_H4=iTime(NULL,240,1);
//--- determining the bar index on H4
   int Bar_Close_H4=iBarShift(NULL,240,Time_Close_H4);
//--- determining the price of the line on H4
   double Price_Resistance_H4=ObjectGetValueByShift("TL_Resistance",Bar_Close_H4);
//--- determining the price of the line on H4   
   double Price_Support_H4=ObjectGetValueByShift("TL_Support",Bar_Close_H4);
// 2. Conditions for trend line breakthroughs
//--- for breaking through the support line
   bool breakdown=(Price_Close_H4<Price_Support_H4);
//--- for braking through the resistance line
   bool breakup=(Price_Close_H4>Price_Resistance_H4);
// 3. Delivering the push notifications
   if(breakdown==true)
     {
      //--- send no more than one notification per 4 hours
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the support line");
        }
     }
   if(breakup==true)
     {
      //--- send no more than one notification per 4 hours
      SleepMinutes=240;
      LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the resistance line");
        }
     }
MQL5
// 1. Getting the price parameters of the trend line
   double Close[];
   CopyClose(Symbol(),PERIOD_H4,TimeCurrent(),10,Close);
//--- setting the array indexing order
   ArraySetAsSeries(Close,true);
//---
   datetime Close_time[];
   CopyTime(Symbol(),PERIOD_H4,TimeCurrent(),10,Close_time);
//--- setting the array indexing order
   ArraySetAsSeries(Close_time,true);
//---
   double Price_Support_H4=ObjectGetValueByTime(0,"TL_Support",Close_time[1]);
   double Price_Resistance_H4=ObjectGetValueByTime(0,"TL_Resistance",Close_time[1]);
// 2. Conditions for trend line breakthroughs
   bool breakdown=(Close[1]<Price_Support_H4);
   bool breakup=(Close[1]>Price_Resistance_H4);
ol>

Um einen Breakthrough zu identifizieren, habe ich die MQL4-Funktion ObjectGetValueByShift() und die MQL5-Funktion ObjectGetValueByTime() verwendet.

Ich hätte womöglich ganz einfach eine 1 anstelle von Bar_Close_H4 als Parameter für ObjectGetValueByShift() einstellen können, allerdings entschied ich mich dazu, zuerst den H4-Index zu bestimmen. Was die Limitierung der Zahl der gesendeten Nachrichten angeht, so habe ich mich der Lösung bedient, auf die ich in diesem Forenthread hier gestoßen bin. Ich möchte dem entsprechenden Autor an dieser Stelle meinen Dank aussprechen.

7. Der praktische Nutzen von Trendlinien fürs Trading

Der einfachste und simpelste Weg: Identifizieren Sie einen Breakthrough, warten Sie auf den Pullback und betreten Sie danach den Markt.

Idealerweise sollten Sie in etwa Folgendes erhalten:

Abb. 3. Trendlinien-Breakthrough

Abb. 3. Trendlinien-Breakthrough

 Sie können Ihr Vorstellungsvermögen bemühen und versuchen, die Formationen zu identifizieren, beispielsweise die Muster der technischen Analyse wie zum Beispiel ein Dreieck:

Abb. 4. Dreiecksmuster

Abb. 4. Dreiecksmuster

In den Bildern oben wurden die Linien nicht durch einen kleinen Zeitrahmen verdeutlicht.

Fazit

Damit möchte ich diesen Artikel beenden. Ich hoffe, Sie fanden ihn zumindest ein wenig nützlich. Der Artikel richtete sich an Anfänger und Amateure, so wie ich einer bin.

Während des Schreibens dieses Artikels habe ich eine ganze Menge gelernt und erfahren: das Schreiben bedeutsamer Kommentare über Codezeilen als auch meinen Sinneswandel ausgehend von einer komplexen/umständlichen Lösung zur Klarstellung von Extrempunkten hinzu einer ungleich simpleren.

Seien Sie für das Lesen dieses Artikels ganz herzlich bedankt. Über Feedback würde ich mich sehr freuen.