Indikator zur Erzeugung eines Three Line Break-Charts

Dmitriy Zabudskiy | 2 Juni, 2016

Einleitung

In älteren Beiträgen wurden bereits Point-&Figure-, Kagi- und Renko-Charts besprochen. In Fortführung der Beitragsreihe zu Charts des 20. Jahrhunderts, werden wir uns diesmal mit dem Three Line Break-Chart beschäftigen, oder, genauer gesagt, mit seiner Implementierung mittels eines Programmcodes. Über den Ursprung dieses Charts ist nur wenig bekannt. Ich glaube, seine Anfänge liegen in Japan. In den USA erfuhr man von diesem Chart aus dem Buch "Beyond Candlesticks" von Steve Nison, das 1994 veröffentlicht wurde.

Genauso wie in den oben erwähnten Charts, berücksichtigt man bei der Konstruktion des Three Line Break Charts den Zeitbereich nicht. Sie beruht auf neu entstandenen Schlusskursen eines gewissen Zeitrahmens, sodass kleine Preisfluktuationen in Bezug auf die vorherige Bewegung gefiltert werden können.

In seinem Buch "Beyond Candlesticks" beschreibt Steve Nison 11 Prinzipien einer grafischen Darstellung dieses Charts (S. 185). Ich habe sie zu drei Prinzipien zusammengefasst.

Betrachten wir uns die Beispiele einer klassischen Chartkonstruktion auf Basis von historischen Daten (Abb. 1) mal genauer.

Abb. 1 Beispiel der Konstruktion eines Three Line Break Charts (EURUSD H1 27.06.2014)

Abb. 1 Beispiel der Konstruktion eines Three Line Break Charts (EURUSD H1 27.06.2014)

Abb. 1 zeigt links ein Kerzenchart und rechts ein Three Line Break Chart Dies ist ein Chart von EURUSD, Zeitrahmen H1. Das Startdatum des Charts ist der 27.06.2014 zum Kurs von 1,3613 (Schlusszeit der Kerze ist 00:00); die Kerze schließt dann um 01:00 bei 1,3614, und bildet die erste steigende Linie des Three Line Break Charts. Die folgende Kerze in Richtung einer Baisse (02:00) bildet eine steigende Linie und schließt bei 1,3612 (Schlusskurs ist geringer als das vorangegangene Minimum).

Die Hausse-Kerzen bewegen den Kurs in Richtung der 1,3619 (03:00) Markierung, und bilden somit ein neues Maximum und eine Linie. Die Kerze um 04:00 ist nicht unter das Minimum gefallen und hat die Konstruktion nicht beeinflusst. Die Kerze um 05:00 schließt bei 1,3623 und markiert ein neues Maximum (neue steigende Linie).

Um die Abwärtsbewegung auszudehnen, müssen wir zwei Minima passieren (1,3613), doch die Hausse wird ihre Position nicht verlassen und bildet ein neues Maximum 1,3626 (06:00). Danach versucht die Hausse den Aufwärtstrend zwei Stunden lang umzukehren, doch geht der gleiche Trend weiter, mit einem neuen Maximum bei 1,3634 (09:00). Die Hausse ist bestimmend. Um eine steigende Linie zeichnen zu können, müssen drei Minima passiert werden (1,3626; 1,3623 und 1m3619).

Wie wir deutlich sehen, hat in den folgenden drei Stunden die Baisse den Markt übernommen und ihn bis auf 1,3612 (12:00) abgesenkt. Dies ist in einer neuen steigenden Linie abgebildet. Die folgenden fünf Stunden jedoch zeigen, dass die Hausse ihre Position zurückgewinnt und den Markt wieder auf 1,3641 zurückbringt, das vorherige Maximum von 1,3626 passiert und um 17:00 eine neue steigende Linie bildet. Die Baisse passiert das vorherige Minimum um 18:00 nicht, und in den kommenden fünf Stunden bringt die Hausse den Markt wieder auf 1,3649 hoch, und bildet somit stündlich eine neue steigende Linie.


Grundlagen der Chartkonstruktion

Bevor wir zum Code kommen, sprechen wir zunächst ein wenig über den Indikator an sich und versuchen herauszufinden, was ihn von anderen unterscheidet. Es ist offensichtlich, dass das Three Line Break, wie andere Indikatoren auch, zur Erleichterung einer effizienten Marktanalyse und der Suche nach neuen Strategien konzipiert wurde. Ich bin sicher, Sie möchten gerne wissen, ob es Neuerungen gibt. Ja gibt es, und zwar einige. Der Indikator erlaubt die Änderung des Kurstyps für eine Berechnung. Er deckt alle vier Standard-Barkurse ab. Der klassische Typ ist für die Konstruktion von Charts für nur einen Kurstyp konzipiert , wohingegen der moderne Typ die Verwendung aller vier Kurstypen zulässt (offen, hoch, niedrig und geschlossen). Er verändert das Aussehen der klassischen Chartkonstruktion durch Hinzufügen von "Schatten" an den Linien, sodass sie wie japanische Kerzen aussehen, was das visuelle Verständnis des Charts ergänzt.

Die modernere Version bietet auch Merkmale zur Synchronisierung von Kursdaten zur Zeit und ersetzt so fehlende Kurse für die wichtigsten (Prioirität).

Abb. 2 zeigt die modernere Art der Chartkonstruktion:

Abb. 2 Veränderter Chart auf Grundlage von vier Kurstypen

Abb. 2 Veränderter Chart auf Grundlage von vier Kurstypen

Da die modernere Konstruktion vier Three Line Break Charts zu unterschiedlichen Kurstypen zusammenführt, kommt es ganz natürlicherweise zu Abweichungen zwischen den Kursen. Um dies zu vermeiden ist Datensynchronisierung erforderlich. Die Kurssynchronisierung wurde auf zwei Arten ausgeführt: komplett (Abb. 2 rechts) und teilweise (Abb. 2 links). Komplette Synchronisierung repräsentiert eine gefilterte teilweise, wo alle Daten auf den Chart gezeichnet werden und fehlende Daten durch die Prioritätskurse ersetzt werden, die in den Einstellungen angegeben wurden. Im Modus 'komplette Synchronisierung' werden fehlende Daten einfach weggelassen und nur Kerzen mit einem kompletten Datenset gezeichnet.

Eine weitere Neuerung ist der Zeitraum-Trenner, der für bequemes Signalsplitting eingeführt wurde. Wie Sie ja wissen, kann man den Zeitraum-Trenner in den Einstellungen des Charts aktivieren. Sie ändern sich im Indikator, je nach dem, in den Einstellungen festgelegten, Zeitrahmen. Im Gegensatz zu den Charts in MetaTrader 5, wo Zeiträume durch eine senkrechte gestrichelte Linie getrennt werden, wird in diesem Indikator ein neuer Zeitraum durch Veränderung der Linienfarbe dargestellt (Kerzen, Abb. 3):

Abb. 3 Zeitraum-Trenner im Indikator

Abb. 3 Zeitraum-Trenner im Indikator

Eine weitere Ergänzung ist die Implementierung eines technischen Indikators iMA, der auf den Kursen des Hauptcharts aufgebaut, doch mit den Indikatordaten zur Zeit synchronisiert ist. Somit werden Daten durch den gleitenden Mittelwert gefiltert (Abb. 4):

Abb. 4 Interner gleitender Mittelwert

Abb. 4 Interner gleitender Mittelwert

Der Indikator besitzt auch ein Merkmal zur Einrichtung einer Mindestbewegung in Punkten zum Zeichnen einer Linie und die Anzahl der für eine Umkehrung erforderlichen Linien. Er erfüllt auch die Rolle eines Filters.


Code des Indikators

Der Algorithmus des Indikators ist eher einfach und umfasst drei Etappen: Daten kopieren, Berechnung auf Basis der kopierten Daten und Füllen der Indikatorpuffer (Konstruktion eines Charts auf Basis der empfangenen Daten). Der Code ist in Funktionen aufgeteilt, die entweder mit sich selbst oder mit den Eingabedaten verbunden sind. Sehen wir uns den Code jetzt mal genauer an.

1. Eingabeparameter des Indikators

Die Eingangsformel des Indikators enthält die Deklarierung der grafischen Konstruktionen. Davon gibt es im Indikator zwei: Chart "ABCTB" (DRAW_COLOR_CANDLES) und zusätzlicher gleitender Mittelwert "LINE_TLB" (DRAW_LINE). Entsprechend gibt es sechs Puffer. Danach kommen die Daten des enum-Typs zur Verbesserung der Schnittstellen-Einstellungen und der Einstellungen an sich:

Dann deklarieren wir alle für die Berechnung notwendigen Puffer-Arrays, Variablen und Strukturen.

//+------------------------------------------------------------------+
//|                                                        ABCTB.mq5 |
//|                                 "Azotskiy Aktiniy ICQ:695710750" |
//|                        "" |
//+------------------------------------------------------------------+
// ABCTB - Auto Build Chart Three Line Break
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      ""
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2
//--- plot ABCTB
#property indicator_label1  "ABCTB"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrBlue,clrRed,clrGreenYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot LINE_TLB
#property indicator_label2  "LINE_TLB"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- Price type for calculation
enum type_price
  {
   close=0, // Close
   open=1,  // Open
   high=2,  // Hight
   low=3,   // Low
  };
//--- type of chart construction
enum type_build
  {
   classic=0,  // Classic
   modified=1, // Modified
  };
//--- priority
enum priority
  {
   highest_t=4, // Highest
   high_t=3,    // High
   medium_t=2,  // Medium
   low_t=1,     // Low
  };
//--- input parameters
input long               magic_numb=65758473787389;                // Magic number
input ENUM_TIMEFRAMES    time_frame=PERIOD_CURRENT;                // Calculation time range
input ENUM_TIMEFRAMES    time_redraw=PERIOD_M1;                    // Period of chart updates
input datetime           first_date_start=D'2013.03.13 00:00:00';  // Start date
input type_price         chart_price=close;                        // Price type for calculation (0-Close, 1-Open, 2-High, 3-Low)
input int                step_min_f=4;                             // Minimum step for a new column (>0)
input int                line_to_back_f=3;                         // Number of lines to display a reversal(>0)
input type_build         chart_type=classic;                       // Type of chart construction (0-classic, 1-modified)
input bool               chart_color_period=true;                  // Changing color for a new period
input bool               chart_synchronization=true;               // Constructing a chart only upon complete synchronization
input priority           chart_priority_close=highest_t;           // Priority of the closing price
input priority           chart_priority_open=highest_t;            // Priority of the opening price
input priority           chart_priority_high=highest_t;            // Priority of the maximum price
input priority           chart_priority_low=highest_t;             // Priority of the minimum price
input bool               ma_draw=true;                             // Draw the average
input ENUM_APPLIED_PRICE ma_price=PRICE_CLOSE;                     // Price type for constructing the average
input ENUM_MA_METHOD     ma_method=MODE_EMA;                       // Construction type
input int                ma_period=14;                             // Averaging period
//--- indicator buffers
//--- buffer of the chart
double         ABCTBBuffer1[];
double         ABCTBBuffer2[];
double         ABCTBBuffer3[];
double         ABCTBBuffer4[];
double         ABCTBColors[];
//--- buffer of the average
double         LINE_TLBBuffer[];
//--- variables
MqlRates rates_array[];// bar data array for analysis
datetime date_stop;    // current date
datetime date_start;   // start date variable for calculation
//+------------------------------------------------------------------+
//| Struct Line Price                                                |
//+------------------------------------------------------------------+
struct line_price// structure for storing information about the past lines
  {
   double            up;  // value of the high price
   double            down;// value of the low price
  };
//+------------------------------------------------------------------+
//| Struct Line Information                                          |
//+------------------------------------------------------------------+
struct line_info// structure for storing information about the shared lines
  {
   double            up;
   double            down;
   char              type;
   datetime          time;
  };
line_info line_main_open[];  // data on the opening prices chart
line_info line_main_high[];  // data on the maximum prices chart
line_info line_main_low[];   // data on the minimum prices chart
line_info line_main_close[]; // data on the closing prices chart
//+------------------------------------------------------------------+
//| Struct Buffer Info                                               |
//+------------------------------------------------------------------+
struct buffer_info// structure for storing data for filling a buffer
  {
   double            open;
   double            high;
   double            low;
   double            close;
   char              type;
   datetime          time;
  };
buffer_info data_for_buffer[];// data for filling the modified construction buffer
datetime array_datetime[];    // array for storing information of the time for every line
int time_array[3];            // array for the function func_date_color
datetime time_variable;       // variable for the function func_date_color
bool latch=false;             // variable-latch for the function func_date_color
int handle;                   // handle of the indicator iMA
int step_min;                 // variable of the minimum step
int line_to_back;             // variable of the number of lines to display a reversal

2. OnInit Funktion 

Alle Indikatorpuffer werden in der OnInit Funktion deklariert, und die Array-Angabe wird wie in einer Zeitreihe eingerichtet.

Danach richten wir die Werte des Indikators ein, die nicht auf dem Chart wiedergegeben werden sollen, richten den Namen ein, legen die Exaktheit fest und entfernen aktuelle Werte, da sie das Chart überladen. Hier richten wir auch den Handle des iMA-Indikators ein und prüfen die Richtigkeit der eingegebenen Daten. Bei einem Fehler wird eine entsprechende Meldung gedruckt und der Wert für das Minimum geändert.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//--- buffers for a chart
   SetIndexBuffer(0,ABCTBBuffer1,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer1,true);
   SetIndexBuffer(1,ABCTBBuffer2,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer2,true);
   SetIndexBuffer(2,ABCTBBuffer3,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer3,true);
   SetIndexBuffer(3,ABCTBBuffer4,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer4,true);
   SetIndexBuffer(4,ABCTBColors,INDICATOR_COLOR_INDEX);
   ArraySetAsSeries(ABCTBColors,true);
//--- buffer for constructing the average
   SetIndexBuffer(5,LINE_TLBBuffer,INDICATOR_DATA);
   ArraySetAsSeries(LINE_TLBBuffer,true);
//--- set the values that are not going to be reflected on the chart
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); // for the chart
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); // for the average
//--- set the indicator appearance
   IndicatorSetString(INDICATOR_SHORTNAME,"ABCTB "+IntegerToString(magic_numb)); // name of the indicator
//--- accuracy of display
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- prohibit displaying the results of the indicator current value
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(1,PLOT_SHOW_DATA,false);
//---
   handle=iMA(_Symbol,time_frame,ma_period,0,ma_method,ma_price);
   if(step_min_f<1)
     {
      step_min=1;
      Alert("Minimum step for a new column must be greater than zero");
     }
   else step_min=step_min_f;
//---
   if(line_to_back_f<1)
     {
      line_to_back=1;
      Alert("The number of lines to display a reversal must be greater than zero");
     }
   else line_to_back=line_to_back_f;
//---
   return(INIT_SUCCEEDED);
  }

3. Funktion zum Kopieren der Daten

Da der Indikator für die Arbeit mit allen vier Kurstypen ausgerichtet ist, müssen unbedingt alle Daten kopiert werden - einschließlich der Zeit. In MQL5 gibt es eine Struktur namens MqlRates. Sie wird zur Speicherung von Information über die Zeit des Beginns einer Handelssitzung, der Kurse, Volumen und des Spreads verwendet.

Die Eingabeparameter der Funktion sind das Start- und das Enddatum, der Zeitrahmen und das Zielarray vom Typ MqlRates. War der Kopiervorgang erfolgreich liefert die Funktion 'true'. Die Daten werden in ein Zwischenarray kopiert. Berechnete fehlende Daten plus eine Sitzung werden hierhin kopiert, und die Daten werden permanent erneuert. War der Kopiervorgang in ds Zwischenarray erfolgreich, dann werden die Daten ins Array kopiert und übertragen, um eine saubere Arbeit der Funktion sicherzustellen.

//+------------------------------------------------------------------+
//| Func All Copy                                                    |
//+------------------------------------------------------------------+
bool func_all_copy(MqlRates &result_array[],// response array
                   ENUM_TIMEFRAMES period,  // timeframe
                   datetime data_start,     // start date
                   datetime data_stop)      // end date
  {
//--- declaration of auxiliary variables
   bool x=false;       // variable for the function response
   int result_copy=-1; // copied data count
//--- adding variables and arrays for calculation
   static MqlRates interim_array[]; // temporary dynamic array for storing copied data
   static int bars_to_copy;         // number of bars for copying
   static int bars_copied;          // number of copied bars since the start date
//--- find out the current number of bars in the time range
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop);
//--- count the number of bars to be copied
   bars_to_copy-=bars_copied;
//--- if it is not the first time when data is being copied
   if(bars_copied>0)
     {
      bars_copied--;
      bars_to_copy++;
     }
//--- change the size of the receiving array
   ArrayResize(interim_array,bars_to_copy);
//--- copy data to a temporary array
   result_copy=CopyRates(_Symbol,period,0,bars_to_copy,interim_array);
//--- check the result of copying data
   if(result_copy!=-1) // if copying to the temporary array was successful
     {
      ArrayCopy(result_array,interim_array,bars_copied,0,WHOLE_ARRAY); // copy the data from the temporary array to the main one
      x=true;                   // assign the positive response to the function
      bars_copied+=result_copy; // increase the value of the copied data
     }
//---
   return(x);
  }

4. Funktion zur Berechnung der Daten

Diese Funktion ist ein Prototyp der Datenberechnung für eine klassische Konstruktion des Three Line Break Charts. Wie bereits erwähnt, berechnet die Funktion nur Daten und legt sie in einem spezielle Array des Strukturtyps line_info ab, das zu Beginn des Codes deklariert wird.

Diese Funktion umfasst noch zwei weitere Funktion: func_regrouping (Regruppierungsfunktion) und func_insert (Einfügefunktion). Beide müssen wir uns zunächst einmal ansehen:

4.1 Regruppierungsfunktion

Diese Funktion regruppiert Informationen über aufeinanderfolgende Linien der gleichen Richtung. Sie ist durch die Größe des Arrays, das in sie übertragen wird, begrenzt, oder genauer gesagt durch den Parameter line_to_back_f aus den Indikatoreinstellungen (Anzahl der Linien, die eine Umkehrung anzeigen soll). Jedes Mal wenn also die Kontrolle an die Funktion übertragen wird, verschieben sich alle empfangenen Daten über identische Linien einen Punkt nach hinten und der Index '0' wird mit einem neuen Wert gefüllt.

So wird die Information über die für einen Bruch notwendigen Linien gespeichert (im Falle der klassischen Konstruktion hat dieser Bruch drei Linien).

//+------------------------------------------------------------------+
// Func Regrouping                                                   |
//+------------------------------------------------------------------+
void func_regrouping(line_price &input_array[],// array for regrouping
                     double new_price,         // new price value
                     char type)                // type of movement
  {
   int x=ArraySize(input_array);// find out the size of the array for regrouping
   for(x--; x>0; x--)           // regrouping loop
     {
      input_array[x].up=input_array[x-1].up;
      input_array[x].down=input_array[x-1].down;
     }
   if(type==1)
     {
      input_array[0].up=new_price;
      input_array[0].down=input_array[1].up;
     }
   if(type==-1)
     {
      input_array[0].down=new_price;
      input_array[0].up=input_array[1].down;
     }
  }

 4.2 Einfügefunktion

 Diese Funktion führt das Einfügen der Werte in das Antwort-Array aus. Der Code ist einfach und bedarf keiner detaillierten Erklärung. 

//+------------------------------------------------------------------+
// Func Insert                                                       |
//+------------------------------------------------------------------+
void func_insert(line_info &line_m[],  // target array
                 line_price &line_i[], // source array
                 int index,            // array element being inserted
                 char type,            // type of the target column
                 datetime time)        // date
  {
   line_m[index].up=line_i[0].up;
   line_m[index].down=line_i[0].down;
   line_m[index].type=type;
   line_m[index].time=time;
  }

Die Funktion zur Datenberechnung wird per Konvention in drei Teile unterteilt. Der erste Teil kopiert mit Hilfe des Wechsel-Operators die gerade analysierten Daten in ein Zwischenarray. Nur der betroffene Kurs wird kopiert. Der zweite Teil setzt einen Testlauf in Gang, um den benötigen Platz im Datenarray zu ermitteln. Danach erfährt das Datenarray line_main_array[], das anfänglich für eine Antwort an die Funktion übertragen, eine Veränderung. Der dritte Teil seinerseits befüllt das angepasste Datenarray.

//+------------------------------------------------------------------+
//| Func Build Three Line Break                                      |
//+------------------------------------------------------------------+
void func_build_three_line_break(MqlRates &input_array[],      // array for analysis
                                 char price_type,              // type of the price under analysis (0-Close, 1-Open, 2-High, 3-Low)
                                 int min_step,                 // minimum step for drawing a line
                                 int line_back,                // number of lines for a reversal
                                 line_info &line_main_array[]) // array for return (response) of the function
  {
//--- calculate the size of the array for analysis
   int array_size=ArraySize(input_array);
//--- extract data required for calculation to an intermediate array
   double interim_array[];// intermediate array
   ArrayResize(interim_array,array_size);// adjust the intermediate array to the size of the data
   switch(price_type)
     {
      case 0: // Close
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].close;
           }
        }
      break;
      case 1: // Open
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].open;
           }
        }
      break;
      case 2: // High
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].high;
           }
        }
      break;
      case 3: // Low
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].low;
           }
        }
      break;
     }
//--- enter the variables for storing information about current situation
   line_price passed_line[];// array for storing information about the latest prices of the lines (type structure line_price)
   ArrayResize(passed_line,line_back+1);
   int line_calc=0;// number of lines
   int line_up=0;// number of the last ascending lines
   int line_down=0;// number of the last descending lines
   double limit_up=0;// upper limit necessary to pass
   double limit_down=0;// lower limit necessary to pass
/* Fill variables informing of the current situation with the first values */
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- start the first loop to calculate received data for filling a buffer for drawing
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// no lines have been drawn
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            line_calc++;// update the line counter
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            line_calc++;// update the line counter
            line_down++;
           }
        }
      if(line_up>line_down)// last ascending line (lines)
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            line_calc++;// update the line counter
            line_up++;
           }
         if(interim_array[x]<limit_down)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            line_calc++;// update the line counter
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// last descending line (lines)
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            line_calc++;// update the line counter
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            line_calc++;// update the line counter
            line_down++;
           }
        }
     }
   ArrayResize(line_main_array,line_calc);// change the size of the target array
//--- zeroise variables and fill with the the initial data
   line_calc=0;
   line_up=0;
   line_down=0;
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- start the second loop to fill a buffer for drawing
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// no lines have been drawn
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// update the line counter
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// update the line counter
            line_down++;
           }
        }
      if(line_up>line_down)// last ascending line (lines)
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// update the line counter
            line_up++;
           }
         if(interim_array[x]<limit_down)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// update the line counter
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// last descending line (lines)
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// the upper limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],1);// regroup
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// update the line counter
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// the lower limit has been passed
           {
            func_regrouping(passed_line,interim_array[x],-1);// regroup
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// update the line counter
            line_down++;
           }
        }
     }
  }

5. Funktion zur Konstruktion des Charts

Zweck dieser Funktion ist die Berechnung der Daten für ein Chart auf Basis des ausgewählten Konstruktionsparameters (klassisch oder verändert) und für das Befüllen des Indikatorpuffers mit den Daten für eine Anzeige. Diese Funktion der Chartkonstruktion hat, genauso wie die vorherige Funktion, ebenfalls drei zusätzliche Funktionen, nämlich die Funktion für die Farbe, die Funktion für die Synchronisierung und die Funktion für den gleitenden Mittelwert. Betrachten wir Sie uns mal im Detail.

5.1 Farbfunktion

Diese Funktion hat nur einen Eingabeparameter - Zeit. Die Antwort der Funktion ist eine boole'sche Variable. Stellen die übertragenen Daten die Grenze des Zeitraums dar, liefert die Funktion 'true'. Da Zeiträume vom ausgewählten Zeitrahmen abhängen, besitzt die Funktion eine eingebaute Trennung des Zeitraums mittels des bedingten Operators falls. Nachdem der Zeitraum ausgewählt worden ist, durchläuft er eine Prüfung, ob ein neuer Zeitraum bereits begonnen hat. Dies geschieht mittels Umwandlung eines Datums in die MqlDateTime Struktur sowie einen Vergleich. Für den Zeitrahmen bis zu und inkl. H2, geben Veränderungen im Wert des Datums den Start eines neuen Zeitraums an. Zeitrahmen von H12 - D1 inklusive, geben Änderungen in den Monaten an, und zwischen W1 und MN überprüfen wir die Veränderung des Jahr.

Leider enthält die Struktur MqlDateTime keine Informationen über die aktuelle Woche. Dieses Problem wurde durch Erzeugung eines Anfangspunkts gelöst, der durch die Variable time_variable dargestellt ist. Im weiteren Verlauf der Linie wird eine Anzahl von Sekunden in einer Woche von diesem Datum abgezogen.

//+------------------------------------------------------------------+
// Func Date Color                                                   |
//+------------------------------------------------------------------+
bool func_date_color(datetime date_time) // input date
  {
   bool x=false;// response variable
   int seconds=PeriodSeconds(time_frame);// find out the calculation time range
   MqlDateTime date;
   TimeToStruct(date_time,date);// convert data
   if(latch==false) // check the state of the latch
     {
      MqlDateTime date_0;
      date_0=date;
      date_0.hour=0;
      date_0.min=0;
      date_0.sec=0;
      int difference=date_0.day_of_week-1;
      datetime date_d=StructToTime(date_0);
      date_d=date_d-86400*difference;
      time_variable=date_d;
      latch=true;// lock the latch
     }
   if(seconds<=7200)// period is less than or equal to H2
     {
      if(time_array[0]!=date.day)
        {
         x=true;
         time_array[0]=date.day;
        }
     }
   if(seconds>7200 && seconds<=43200)// period is greater than H2 but less than or equal to H12
     {
      if(time_variable>=date_time)
        {
         x=true;
         time_variable=time_variable-604800;
        }
     }
   if(seconds>43200 && seconds<=86400)// period is greater than H12 but less than or equal to D1
     {
      if(time_array[1]!=date.mon)
        {
         x=true;
         time_array[1]=date.mon;
        }
     }
   if(seconds>86400)// period W1 or MN
     {
      if(time_array[2]!=date.year)
        {
         x=true;
         time_array[2]=date.year;
        }
     }
   return(x);
  }

5.2 Synchronisierungsfunktion

Die Synchronisierungsfunktion besitzt sechs Eingabeparameter: vier davon für die Priorität der Kurse, einen boole'schen Parameter der kompletten oder teilweisen Synchronisierung und das gerade analysierte Array selbst. Die Funktion ist in zwei Teile unterteilt - für kompletter und teilweise Synchronisierung.

Komplette Synchronisierung wird in drei Etappen ausgeführt:

  1. Berechnung der Array-Elemente zur Erfüllung der Bedingung die Daten auf allen vier Kurstypen zu enthalten.
  2. Kopieren von Elementen in ein Zwischenarray unter der gleichen Bedingung.
  3. Kopieren vom Zwischenarray in das, von den Parametern übertragene Array.

Teilweise Synchronisierung ist etwas komplexer.

Das übertragene eindimensionale Strukturarray wird in ein zweidimensionales umgewandelt, in dem der erste Index die Reihenfolge und der zweite den Kurstyp bezeichnet. Dann wird ein eindimensionales Array mit vier Elementen eingeführt. In dieses Array werden Prioritätsebenen für Kurse kopiert und danach wird das Array sortiert, um die Reihenfolge der Prioritäten zu bestimmen. Danach führen wir die Verteilung gemäß der Prioritäten mit Hilfe der Schleife für und dem bedingten Operator falls aus. Sind die Prioritäten gleich, sieht die Abfolge der Kurse gleichzeitig so aus: schließen, öffnen, hoch, niedrig. Sobald der Operator falls den ersten priorisierten Wert findet, ersetzt die Schleife für alle Null-Daten im zuvor angelegten zweidimensionalen Array für die Prioritätskurse, usw.

//+------------------------------------------------------------------+
// Func Synchronization                                              |
//+------------------------------------------------------------------+
void func_synchronization(buffer_info &info[],
                          bool synchronization,
                          char close,
                          char open,
                          char high,
                          char low)
  {
   if(synchronization==true)// carry out a complete synchronization
     {
      int calc=0;// count variable
      for(int x=0; x<ArraySize(info); x++)// count complete data
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)calc++;
        }
      buffer_info i_info[];    // enter a temporary array for copying
      ArrayResize(i_info,calc);// change the size of the temporary array
      calc=0;
      for(int x=0; x<ArraySize(info); x++)// copy data into the temporary array
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)
           {
            i_info[calc]=info[x];
            calc++;
           }
        }
      ZeroMemory(info);        // clear the target array
      ArrayResize(info,calc);  // change the size of the main array
      for(int x=0; x<calc; x++)// copy data from the temporary array to the main one
        {
         info[x]=i_info[x];
        }
     }
   if(synchronization==false)  // change zero values to priority ones
     {
      int size=ArraySize(info); // measure the size of the array
      double buffer[][4];       // create a temporary array for calculation
      ArrayResize(buffer,size); // change the size of the temporary array
      for(int x=0; x<size; x++) // copy data into the temporary array
        {
         buffer[x][0]=info[x].close;
         buffer[x][1]=info[x].open;
         buffer[x][2]=info[x].high;
         buffer[x][3]=info[x].low;
        }
      char p[4];// enter an array for sorting by the order
      p[0]=close; p[1]=open; p[2]=high; p[3]=low;// assign variables for further sorting
      ArraySort(p); // sort
      int z=0,v=0;  // initialize frequently used variables
      for(int x=0; x<4; x++)// taking into account the results of the sorting, look through all variables and substitute them according to the priority
        {
         if(p[x]==close)// priority is for the closing prices
           {
            for(z=0; z<size; z++)
              {
               for(v=1; v<4; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][0];
                 }
              }
           }
         if(p[x]==open)// priority is for the opening prices
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=1 && buffer[z][v]==0)buffer[z][v]=buffer[z][1];
                 }
              }
           }
         if(p[x]==high)// priority is for the maximum prices
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=2 && buffer[z][v]==0)buffer[z][v]=buffer[z][2];
                 }
              }
           }
         if(p[x]==low)// priority is for the minimum prices
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<3; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][3];
                 }
              }
           }
        }
      for(int x=0; x<size; x++)// copy data from the temporary array back
        {
         info[x].close=buffer[x][0];
         info[x].open=buffer[x][1];
         info[x].high=buffer[x][2];
         info[x].low=buffer[x][3];
        }
     }
  }

5.3 Funktion des gleitenden Mittelwerts

Das ist die einfachste Funktion. Mit Hilfe des Indikator-Handles, der in der OnInit Funktion erhalten wurde, kopieren wir den, dem in die Parametern der Funktion übertragenen Datum entsprechenden Wert. Dieser Wert wird dann als eine Antwort an diese Funktion übertragen.

//+------------------------------------------------------------------+
// Func MA                                                           |
//+------------------------------------------------------------------+
double func_ma(datetime date)
  {
   double x[1];
   CopyBuffer(handle,0,date,1,x);
   return(x[0]);
  }

Die Funktion zur grafischen Darstellung eines Charts ist per Konvention in zwei Teile unterteilt: klassische Darstellung und veränderte Darstellung. Die Funktion besitzt zwei Eingabeparameter: Kurstyp für die Konstruktion (wird während veränderter Konstruktion ignoriert) und Typ der Konstruktion (klassisch oder modifiziert).

Die Indikatorpuffer werden ganz zu Anfang geleert und dann, je nach Typ der Konstruktion, in zwei Teile unterteilt. Der erste Teil (wir sprechen hier von veränderter Konstruktion) beginnt mit dem Aufruf der Funktion zur Berechnung aller vier Preistypen. Danach legen wir ein gemeinsames Datenarray an, in das wir die verwendeten Daten kopieren, die beim Aufruf der Funktion zur Datenberechnung empfangen wurden. Dann wird das Array mit den empfangen Daten sortiert und von sich wiederholenden Daten geleert . Danach wird das Array data_for_buffer[], das auf globaler Ebene deklariert wurde, auf Basis der aufeinander folgenden Daten mit der folgenden Datensynchronisierung befüllt. Und zum Schluss der veränderten Konstruktion werden die Indikatorpuffer befüllt.

Der zweite Teil (klassische Konstruktion) ist weitaus einfacher. Zuerst wird die Funktion zur Datenberechnung aufgerufen und dann werden die Indiaktor-Puffer befüllt.

//+------------------------------------------------------------------+
//| Func Chart Build                                                 |
//+------------------------------------------------------------------+
void func_chart_build(char price, // price type for chart construction
                      char type)  // type of chart construction
  {
//--- Zeroise the buffers
   ZeroMemory(ABCTBBuffer1);
   ZeroMemory(ABCTBBuffer2);
   ZeroMemory(ABCTBBuffer3);
   ZeroMemory(ABCTBBuffer4);
   ZeroMemory(ABCTBColors);
   ZeroMemory(LINE_TLBBuffer);
   if(type==1)// construct a modified chart (based on all price types)
     {
      func_build_three_line_break(rates_array,0,step_min,line_to_back,line_main_close);// data on closing prices
      func_build_three_line_break(rates_array,1,step_min,line_to_back,line_main_open);// data on opening prices
      func_build_three_line_break(rates_array,2,step_min,line_to_back,line_main_high);// data on maximum prices
      func_build_three_line_break(rates_array,3,step_min,line_to_back,line_main_low);// data on minimum prices
      //--- calculate data arrays
      int line_main_calc[4];
      line_main_calc[0]=ArraySize(line_main_close);
      line_main_calc[1]=ArraySize(line_main_open);
      line_main_calc[2]=ArraySize(line_main_high);
      line_main_calc[3]=ArraySize(line_main_low);
      //--- gather the date array
      int all_elements=line_main_calc[0]+line_main_calc[1]+line_main_calc[2]+line_main_calc[3];// find out the number of all elements
      datetime datetime_array[];// enter the array for copying
      ArrayResize(datetime_array,all_elements);
      int y[4];
      ZeroMemory(y);
      for(int x=0;x<ArraySize(datetime_array);x++)// copy data into the array
        {
         if(x<line_main_calc[0])
           {
            datetime_array[x]=line_main_close[y[0]].time;
            y[0]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1] && x>=line_main_calc[0])
           {
            datetime_array[x]=line_main_open[y[1]].time;
            y[1]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1]+line_main_calc[2] && x>=line_main_calc[0]+line_main_calc[1])
           {
            datetime_array[x]=line_main_high[y[2]].time;
            y[2]++;
           }
         if(x>=line_main_calc[0]+line_main_calc[1]+line_main_calc[2])
           {
            datetime_array[x]=line_main_low[y[3]].time;
            y[3]++;
           }
        }
      ArraySort(datetime_array);// sort the array
      //--- delete replicated data from the array
      int good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// count useful information
        {
         if(datetime_array[x-1]!=datetime_array[x])good_info++;
        }
      ArrayResize(array_datetime,good_info);
      array_datetime[0]=datetime_array[0];// copy the first element as it is the pattern in the beginning of comparison
      good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// fill the new array with useful data
        {
         if(datetime_array[x-1]!=datetime_array[x])
           {
            array_datetime[good_info]=datetime_array[x];
            good_info++;
           }
        }
      //--- fill the buffer for drawing (colored candles)
      int end_of_calc[4];// variables of storing information about the last comparison
      ZeroMemory(end_of_calc);
      ZeroMemory(data_for_buffer);
      ArrayResize(data_for_buffer,ArraySize(array_datetime));// change the size of the declared global array for storing data before passing it to a buffer
      for(int x=0; x<ArraySize(array_datetime); x++)
        {
         data_for_buffer[x].time=array_datetime[x];
         for(int s=end_of_calc[0]; s<line_main_calc[0]; s++)
           {
            if(array_datetime[x]==line_main_close[s].time)
              {
               end_of_calc[0]=s;
               if(line_main_close[s].type==1)data_for_buffer[x].close=line_main_close[s].up;
               else data_for_buffer[x].close=line_main_close[s].down;
               break;
              }
           }
         for(int s=end_of_calc[1]; s<line_main_calc[1]; s++)
           {
            if(array_datetime[x]==line_main_open[s].time)
              {
               end_of_calc[1]=s;
               if(line_main_open[s].type==1)data_for_buffer[x].open=line_main_open[s].down;
               else data_for_buffer[x].open=line_main_open[s].up;
               break;
              }
           }
         for(int s=end_of_calc[2]; s<line_main_calc[2]; s++)
           {
            if(array_datetime[x]==line_main_high[s].time)
              {
               end_of_calc[2]=s;
               data_for_buffer[x].high=line_main_high[s].up;
               break;
              }
           }
         for(int s=end_of_calc[3]; s<line_main_calc[3]; s++)
           {
            if(array_datetime[x]==line_main_low[s].time)
              {
               end_of_calc[3]=s;
               data_for_buffer[x].low=line_main_low[s].down;
               break;
              }
           }
        }
      //--- start the function of synchronizing data
      func_synchronization(data_for_buffer,chart_synchronization,chart_priority_close,chart_priority_open,chart_priority_high,chart_priority_low);
      //--- preparatory actions before starting the function func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- fill the buffer for drawing candles
      for(int x=ArraySize(data_for_buffer)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=data_for_buffer[x].open;
         ABCTBBuffer2[z]=data_for_buffer[x].high;
         ABCTBBuffer3[z]=data_for_buffer[x].low;
         ABCTBBuffer4[z]=data_for_buffer[x].close;
         if(ABCTBBuffer1[z]<=ABCTBBuffer4[z])ABCTBColors[z]=0;
         if(ABCTBBuffer1[z]>=ABCTBBuffer4[z])ABCTBColors[z]=1;
         if(func_date_color(data_for_buffer[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(data_for_buffer[x].time);
         z++;
        }
     }
   else// construct a classic chart (based on one price type)
     {
      func_build_three_line_break(rates_array,price,step_min,line_to_back,line_main_close);// find data on selected prices
      ArrayResize(array_datetime,ArraySize(line_main_close));
      //--- preparatory actions before starting the function func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- the buffer for drawing candles
      for(int x=ArraySize(line_main_close)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=line_main_close[x].up;
         ABCTBBuffer2[z]=line_main_close[x].up;
         ABCTBBuffer3[z]=line_main_close[x].down;
         ABCTBBuffer4[z]=line_main_close[x].down;
         if(line_main_close[x].type==1)ABCTBColors[z]=0;
         else ABCTBColors[z]=1;
         if(func_date_color(line_main_close[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(line_main_close[x].time);
         z++;
        }
     }
  }

6. Konsolidierungsfunktion

Diese Funktion vereinheitlicht alle kontrollierenden Indikatorelemente. Zunächst wird das aktuelle Datum definiert und danach werden die Funktion zum Kopieren von Daten und die Funktion der Chartkonstruktion aufgerufen.

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_consolidation()
  {
//--- defining the current date
   date_stop=TimeCurrent();
//--- copying data for analysis
   func_all_copy(rates_array,time_frame,first_date_start,date_stop);
//--- basic construction of the chart
   func_chart_build(chart_price,chart_type);
   ChartRedraw();
  }

7. Funktion zur Tastatur-kontrollierten und automatisch kontrollierten Konstruktion

Diese Funktionen dienen einer Neuzeichnung des Indikators durch Drücken der "R"-Taste (OnChartEvent) auf der Tastatur oder der automatischen Neuzeichnung, in Übereinstimmung mit dem ausgewählten Zeitbereich (OnCalculate). Die automatische Kontrolle wird durch die Bar-Funktion (func_new_bar) analysiert - eine vereinfachte Version der in IsNewBar beschriebenen Funktion.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   if(func_new_bar(time_redraw)==true)
     {
      func_consolidation();
     };
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- event of a keystroke
   if(id==CHARTEVENT_KEYDOWN)
     {
      if(lparam==82) //--- the key "R" has been pressed
        {
         func_consolidation();
        }
     }
  }
//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//---
   static datetime old_times; // variable of storing old values
   bool res=false;            // variable of the analysis result
   datetime new_time[1];      // time of a new bar
//---
   int copied=CopyTime(_Symbol,period_time,0,1,new_time); // copy the time of the last bar to the cell new_time
//---
   if(copied>0) // everything is ок. data copied
     {
      if(old_times!=new_time[0]) // if the old time of the bar is not equal to the new one
        {
         if(old_times!=0) res=true; // if it is not the first start, then new bar = true
         old_times=new_time[0];     // remember the time of the bar
        }
     }
//---
   return(res);
  }

An dieser Stelle beenden wir unsere Beschreibung des Indikatorcodes und wenden uns seiner Anwendung zu.


Anwendungsbeispiele des Indikators und einer Handelsstrategie

Beginnen wir mit den Hauptanalysestrategien auf Basis der klassischen Chartkonstruktion.

1. Weiße und schwarze Linien als Signale für Kaufen und Verkaufen

Grob können wir von zwei Regeln sprechen:

  1. Regel Nr. 1: Kaufen bei drei aufeinander folgenden steigenden Linien. Verkaufen bei drei aufeinander folgenden fallenden Linien. Drei aufeinander folgenden Linien deuten auf das Auftreten einer Tendenz hin.
  2. Regel Nr. 2: Verkaufen, wenn die Umkehrungslinie unter die drei aufeinander folgenden steigenden Linien fällt. Kaufen, wenn die Umkehrungslinie höher als die drei aufeinander folgenden fallenden Linien ist.

Abbildung 6 zeigt dies gut - sie gibt eine klassische Konstruktion für EURUSD H1 von Anfang 2013 wieder (der analysierte Zeitbereich ist in Abb. 5 dargestellt).

Abb. 5 Analysierter Zeitbereich EURUSD H1

Abb. 5 Analysierter Zeitbereich EURUSD H1

Abb. 6 Klassische Konstruktion des Three Line Break Charts für EURUSD H1, Anfang 2013, Schlusskurse

Abb. 6 Klassische Konstruktion des Three Line Break Charts für EURUSD H1, Anfang 2013, Schlusskurse

Wir können auf dem Chart (Abb. 6) klar das Signal (Regel Nr. 1) zwischen den Punkten 1 und 2 erkennen, also einen Startpunkt für Verkaufen. In diesem Fall beläuft sich der Ertrag auf über 200 Punkte für vier Dezimalstellen. Der folgende Punkt 4 gibt eine günstige Situation zum Kaufen an (wie in Regel Nr. 2). Beim Schließen in Punkt 5 betrug der Gewinn 40 Punkte und beim Schließen in Punkt 6 haben wir einen Breakeven erreicht.

In Punkt 6 erkennen wir ein Signal zum Verkaufen (Regel Nr. 2). Wir erhalten 10 Punkte Gewinn, wenn wir in Punkt 7 schließen und erreichen einen Breakeven, wenn wir in Punkt 8 schließen. Punkte 8 und 9 können nicht als Signale gelten, da sie weder Regel Nr. 1 noch Regel Nr. 2 erfüllen. In Punkt 10 können wir kaufen (Regel Nr. 1) und erhalten zudem einen Gewinn von 20 Punkten beim Schließen in Punkt 11 oder einen Breakeven in Punkt 12. Alle Ziffern wurden aufgerundet.

Im besten Szenario könnten wir mit Hilfe dieser Strategie einen Gewinn von 270 Punkten machen - was ziemlich beeindruckend ist. Im spezifizierten Zeitbereich herrscht jedoch gleichzeitig auch eine intensive Bewegung, die sich auf die Gewinne auswirkt. Im schlimmsten Szenario führt unser Handel zu einem Breakeven - was ja auch nicht so schlecht ist.

Hier sei angemerkt, dass, wenn eine Situation weder Regel Nr. 1 noch Regel Nr. 2 erfüllt, wir auf eine Bestätigung der Umkehrung der Tendenz warten müssen, dies durch eine Linie in der gleichen Richtung wie die Tendenz dargestellt wird.

2. Abstandsgleiche Kanal., Stütz- und Widerstandslinien

Eine andere Handelsstrategie besteht darin, auf den Three Line Break Chart die technische Analyse anzuwenden. Betrachten wir uns dazu Abb. 7:

Abb. 7 Abstandsgleiche Kanal-, Stütz- und Widerstandslinien, GBPUSD H1, Zeitbereich vom 01.03.2014 - 01.05.2014

Abb. 7 Abstandsgleiche Kanal-, Stütz- und Widerstandslinien, GBPUSD H1, Zeitbereich vom 01.03.2014 - 01.05.2014

In Abb. 7 erkennen Sie, das der fallende, abstandsgleiche Kanal in roten Linien gezeichnet ist, der steigende Kanal in blauen und die Stütz- und Winderstandslinien in schwarzen Linien. Es ist klar, dass die erste Widerstandslinie zu einer Stützlinie wird.

3. Kerzen-Muster

Ein veränderter Chart (Two Line Break) auf dem Zeitrahmen M30 für das Paar USDCAD, Anfang 2013 sieht ziemlich interessant aus.

Wir erkennen hier klar japanische Kerzenmuster, die ihre Signale rechtfertigten (Abb. 8).

Abb. 8 Verändertes Three Line Break Chart, USDCAD M30, Anfang 2013, Bruch bei zwei Zeilen

Abb. 8 Verändertes Three Line Break Chart, USDCAD M30, Anfang 2013, Bruch bei zwei Zeilen

Zu Beginn des Charts sehen wir ein Umkehrungsmuster der "Einhüllung" unter Nr. 1, das aus zwei Kerzen besteht: der roten und der ihr vorausgehenden blauen. Nach der steigenden Trendlinie fällt der Markt zu Nummer 2 ab, die ein Umkehrungsmuster mit einer Kerze, dem sog. "Hammer", ist. An diesem Punkt ändert der Markt seine Richtung. Das gleiche passiert in Muster Nr. 3 ("Kreisel"). Das folgende Umkehrungsmuster "Kharami" (Nr. 4) wird durch die Kerze 4 und der großen steigenden Linie direkt daneben abgebildet. Muster Nr. 6 besteht ebenfalls aus zwei Kerzen ("Einhüllungs-Muster"), doch anders als das erste ähnliche Modell dreht es den Markt in die umgekehrte Richtung.

Daher kann man schlussfolgern, dass die Verwendung des Indikators in dieser Art von Analyse akzeptabel ist, doch durchaus Nachteile aufweist, wie ein nur seltenes Auftreten von Signalen und die Möglichkeit einer erheblichen Inanspruchnahme. Diese Strategie muss also auf jeden Fall noch weiter entwickelt werden.

4. Gleitender Mittelwert

Teilweise Veränderung, wie Hinzufügen eines gleitenden Mittelwerts nur bei gezeichneten Linie, bietet neue Analysemöglichkeiten.

Betrachten wir Abb. 9:

Abb. 9 Analyse des gleitenden Mittelwerts, EURUSD H4, des Three Line Break Charts, klassische Konstruktion, vom 01.01.2014 bis 01.07.2014

Abb. 9 Analyse des gleitenden Mittelwerts, EURUSD H4, des Three Line Break Charts, klassische Konstruktion, vom 01.01.2014 bis 01.07.2014

Der obere Teil von Abb. 9 veranschaulicht eine klassische Konstruktion auf Basis der hohen Kurse mit einem gleitenden Mittelwert (Mittelungszeitraum ist 90, geringer Kurs, geglättete Mittelung). Der untere Teil von Abb. 9 zeigt eine klassische Konstruktion auf Basis der niedrigen Kurse mit einem gleitenden Mittelwert (Mittelungszeitraum ist 90, hoher Kurs, geglättete Mittelung).

Im oberen Teil von Abb. 9 kann der gleitende Mittelwert daher als eine Stützlinie und im unteren Teil als das genaue Gegenteil, nämlich eine Widerstandslinie, angesehen werden. Fällt der Kurs auf beiden Charts unterhalb des Mittelwerts handelt es sich um einen fallenden Trend auf dem Markt und es ist besser zu verkaufen. Steigt der Kurs über den Mittelwert, dann ist es Zeit zu kaufen. Nachteil dieser Strategie - sie ist eher für langfristiges Handeln ausgelegt.


Fazit

Zusammenfassend kann ich sagen, dass der Three Line Break Chart konstant gute Signale liefert oder, im schlechtesten Fall, mindestens zu einem Breakeven führt. Die Praxis zeigt, dass der Chart am besten in einem langfristigen Trend angewendet wird. Daher rate ich von seiner Verwendung für einen kurzfristigen Handel ab. Sollte jemand unter Ihnen noch neue Ideen haben, wie man ihn beim Handeln einsetzen kann, dann freue ich mich, diese mit Ihnen zu diskutieren.

Wie immer habe ich versucht, den Code detailliert zu untersuchen. Nochmals: Notieren Sie bitte alle Ideen wie man ihn erweitern, überarbeiten oder optimieren könnte, in den Kommentaren zu diesem Beitrag.