English Русский 中文 Español 日本語 Português
Ein Einblick in Akkumulation/Distribution und Wo Sie sie bekommen können

Ein Einblick in Akkumulation/Distribution und Wo Sie sie bekommen können

MetaTrader 4Beispiele | 22 April 2016, 17:44
1 184 0
Artyom Trishkin
Artyom Trishkin

Einführung

Es ist bekannt, dass der Akkumulation/Distribution A/D Indikator ein interessantes Merkmal besitzt - ein Ausbruch der in das Indikator-Chart gezeichneten Trendlinie deuten an, dass, mit einem gewissen Grad an Wahrscheinlichkeit, ein Ausbruch der Trendlinie in dem Kurs-Chart bevorsteht:

Ich war fest entschlossen diesen Umstand zu überprüfen, und nachdem ich alle Trendlinien in das A/D Chart gezeichnet hat, stellte ich schnell fest, dass der manuelle Ansatz diese Frage anzugehen nicht zweckmäßig wäre. Aus diesem Grund habe ich eine Funktion entwickelt, die automatisch Trendlinien in das A/D Chart zeichnet und Signal-Indikatoren im Kurs-Chart setzt. Ich möchte eine schrittweise Vorgehensweise zeigen, wie alles mit MQL4 umgesetzt werden kann, um weiter in Ihren Handelsrobotern verwendet zu werden.

Dieser Artikel wird nützlich und interessant für diejenigen sein, die neu sind in der Programmierung mit MQL4. unter diesem Gesichtspunkt habe ich versucht die Informationen auf eine einfach zu erfassende Weise zu präsentieren und die einfachsten Codestrukturen verwendet.



1. Aufgabenstellung

Lassen Sie uns zunächst die festzulegende Aufgabe definieren.

Die Funktion wird im allgemeinen Fall einen Kreuzungspunkt zwischen der in das A/D Chart gezeichneten Trendlinie und der Linie des Indikators selbst ermitteln, den Wert, der die Richtung der Kreuzung anzeigt - abwärts oder aufwärts - zurückgeben und Signalanzeigen (Auf/Ab Pfeile) zur Veranschaulichung in das Kurs-Chart setzen.

Brechen wir jetzt diese allgemeine Aufgabe in spezifischere Ziele herunter:

  1. Die Funktion muss in der Lage sein mit jedem Symbol und jedem Zeitrahmen zu arbeiten,
  2. Da die Funktion als Teil eines EA entworfen wurde, muss sich der A/D Indikator nicht im Hauptchart befinden.
  3. Signal Indikatoren müssen nicht unbedingt in dem Kurs-Chart angezeigt werden - alle Berechnungen werden innerhalb der Funktion ausgeführt und werden nur angezeigt, um die den Betrieb der Funktion visuell zu überwachen.
  4. Die Überkreuzungen können in verschiedenen Richtungen auftreten. Abwärts-Überkreuzung des Aufwärtstrends, Aufwärts-Überkreuzung des Aufwärtstrends, Aufwärts-Überkreuzung des Abwärtstrends, Abwärts-Überkreuzung des Abwärtstrends. Die Funktion wird jede solche Überkreuzung erkennen.

Dies ist es, soweit es die Funktionsfähigkeiten betrifft. Betrachten wir nun die Art und Weise, wie unsere Aufgabe zu erfüllen ist.



2. Arrays mit A/D Indikator-Daten füllen

Wenn sie aufgerufen wird, empfängt die Funktion einige Werte: im Moment - das Array zum Speichern der A/D Indikator Daten, die Anzahl an Balken der Historie zur Identifizierung der A/D Chart Extrema, den Instrument- (Symbol) Namen und Zeitrahmen.

Trendlinien werden basierend auf den identifzierten A/D Chart Extrema gezogen, wobei eine Aufwärtstrend-Linie untere Extrema verbindet und eine Abwärtstrend-Linie obere Extrema.

Eine Aufwärtstrend-Linie erfordert zwei untere Extrema, wobei das Extremum mit dem niedrigsten Wert sich auf der linken Seite des Extremums in unmittelbarer Nähe zum aktuellen (Null) Balken befinden muss. Das gleiche gilt für einen Abwärtstrend: Das Extremum mit dem höchsten Wert muss sich links von dem Extremum neben dem Balken befinden.

Wenn kein Extremum, das diese Voraussetzungen erfüllt, identifiziert werden kann, werden auf diesem Tick keine Trendlinien gezogen. wir werden diese Trendlinien "global" nennen:

Außerdem fügen wir zwei weitere Linien zu den "globalen" Trendlinien hinzu, welche die Verwendung von zwei weit rechts befindlichen Extrema erfordern, wobei

  • ein Aufwärtstrend ein unteres Extremem mit einem kleineren Wert benötigt (links von dem unteren Extremum mit einem größeren Wert), und
  • ein Abwärtstrend benötigt ein oberes Extremum mit einem größeren Wert (links von dem oberen Extremum mit einem kleineren Wert).

Wenn kein Extremum, das diese Voraussetzungen erfüllt, identifiziert werden kann, werden auf diesem Tick keine Trendlinien gezogen.

Diese Trendlinien werden als "lokal" bezeichnet werden:

Als Ergebnis, sehen wir es wie folgt:

In den meisten Fällen, werden wir "lokale" Extrema nicht benötigen, aufgrund der Besonderheiten der Identifikation von "globalen" Extrema und dem Zeihen von Trendlinien durch die Verbindung dieser. Die Option für das Suchen und Zeichnen von "lokalen" Extrema darf deaktiviert werden.

Der Funktionsbetrieb benötigt die Verfügbarkeit von globalen Variablen des EAs, insbesondere ein Array zum Speichern von A/D Daten. Schreiben wir es in die globalen Variablen des EAs:

double      TempIND[];    // Array for storing A/D indicator data

Jetzt sollten die Indikatordaten gelesen werden, um das Array zu füllen:

int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf)
{
   if (sy=="" || sy=="0") sy = Symbol();
   ArrayResize(TempIND,nBars);       // Resize the array according to the size passed to the function
   for (int j=0; j<=nBars-1; j++)
      {
         TempIND[j]=iAD(sy,tf,j);    // Write indicator data in the loop into the array
      }
}

Lassen sie uns analysieren, was wir gerade geschrieben haben:

int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf)

Die Definition der Funktion als solche, wo die Parameter übergeben worden sind TempIND[] - der Name des Arrays mit den Indikator-Werten (übergeben durch Verweis), nBars - die Anzahl an Balken der Historie, von denen diese Werte erhalten werden, sy - Instrument-Name und Zeitrahmen tf.

Im Allgemeinen kann der Funktionsaufruf wie folgt sein:

SignalCrossIND(TempIND, 30, NULL, 5);

wobei TempIND der Arrayname ist, 30 ist die Anzahl der Balken, NULL ist das aktuelle Symbol des Charts, und 5 ist M5 Zeitrahmen.

Die nächste Zeile ist:

if (sy=="" || sy=="0") sy = Symbol();

sie prüft den Namen des an die Funktion übergebenen Handelsinstruments und wenn sein Wert NULL ist oder 0, wird das aktuelle Chartsymbol verwendet.

Wir skalieren dann das Array entsprechend des an die Funktion übergebenen Wertes in der nBars Variable und füllen es mit Indikatorwerten von dem Null-Balken zu dem nBars-1 Balken in der Schleife. Da die Array-Zellen mit Null beginnend nummeriert sind, wird die erste Nummer 0 anstatt 1 sein und die letzte Zahl folglich 29 anstatt 30. Aus diesem Grund verwenden wir nBars-1 (in unserem Beispiel, 30-1=29).

In dieser Phase füllt unsere Funktion das Array mit A/D Indikator-Daten. Wir müssen jetzt Extrema in dem A/D Chart Daten-Array identifizieren..



3. Identifikation von Extrema in A/D Indikator Daten-Arrays

Fangen wir an. Der Betrieb der Funktion erfordert nicht nur globale Variablen, sondern auch von der Funktion selbst verwendete Variablen.

In dieser Phase müssen wir zwei weitere (interne) Array deklarieren, die für das Schreiben von oberen Extrema (Hochs) und unteren Extrema (Tiefs) verwendet werden. zwei Arrays in denen wir den Zeitpunkt der Extrema speichern werden und einige Variablen für die Bedürfnisse der Funktion:

// ------------------------------------------------------------------------------------------                     
// -------------------------------- Function variables --------------------------------------
// ------------------------------------------------------------------------------------------                     

   double   PeakUP[], PeakDN[];     // Declare peak/trough arrays
   datetime TimeDN[], TimeUP[];     // Arrays for storing the time of extrema
   int      i, k;                   // Internal variables of the function

und schließlich fortfahren zu deren Identifikation:

//------------------------------------------------------------------
// Filling arrays with data on peaks and troughs
//------------------------------------------------------------------         
    k=0;                                   // Initialize the trough array index
    for (i=2; i<=nBars-1; i++)             // Run through the array of values
       {
          if (TempIND[i]<TempIND[i-1] && 
              TempIND[i+1]>=TempIND[i])    // Trough identified
             {
                ArrayResize(PeakDN, k+1);  // Resize the trough array according to the number of troughs identified
                ArrayResize(TimeDN, k+1);  // Resize the trough time array according to the number of troughs
                PeakDN[k]=TempIND[i];      // Write the trough value into the trough array...
                TimeDN[k]=iTime(sy,tf,i);  // ...and the time array
                k++;                       // Increase the trough array index
             }
        } 
// -----------------------------------------------------------------------------------------------------------                       
    k=0;                                   // Initialize the peak array index
    for (i=2; i<=nBars-1; i++)             // Run through the array of values
       {
          if (TempIND[i]>TempIND[i-1] && 
              TempIND[i+1]<=TempIND[i])    // Peak identified
             {
                ArrayResize(PeakUP, k+1);  // Resize the peak array according to the number of peaks identified
                ArrayResize(TimeUP, k+1);  // Resize the peak time array according to the number of peaks
                PeakUP[k]=TempIND[i];      // Write its value into the peak array... 
                TimeUP[k]=iTime(sy,tf,i);  // ...and the time array
                k++;                       // Increase the peak array index
             }
       }  

Das Obige bedarf noch der Klärung darüber, was gemacht wird und wie es gemacht wird. Zunächst arrangieren wir zwei Schleifen - für die Identifikation von oberen und unteren Extrema.

Gehen wir über zu der ersten Schleife für die Identifikation von Tiefs (untere Extreme):


Wir beginnen mit der Initialisierung der Variable k bis Null - sie wird auf das erste (Null) Element in dem Array zeigen, in das die identifizierten Tiefs geschrieben werden. Wir schleifen dann das früher von den A/D Indikator-Werten gefüllte Array durch, der Startpunkt ist nicht der erste (Null) Index, sondern der dritte, dem Balken 2 (i=2;) entsprechende.

Warum ist das so? Da ein Extremum eine Bruchstelle in dem Indikator-Chart ist, wo der Wert des ersten Balken größer ist als der auf dem zweiten Balken und der Wert des zweiten Balken weniger ist als der auf dem dritten (Tief), setzen wir den Index auf das dritte Element des Array (unter Berücksichtigung, dass die Elemente mit Null beginnend nummeriert sind), vorausgesetzt es kann ein Extremum sein. Null-Balken werden nicht in den Berechnungen verwendet, um Fehlalarme zu unterdrücken.

Dann starten wir unseren ersten Durchlauf und prüfen

  • ober der Wert auf Balken 2 TempIND[i] weniger ist als auf Balken 1 TempIND[i-1] und
  • ob der Wert auf Balken 3 TempIND[i+1] größer ist als auf Balken 2 TempIND[i].

Wenn diese Bedingungen zutreffen (das Extremum ist identifiziert), erhöhen wir zunächst die Tiefs-Array und Tief-Zeitpunkt-Array Größe um 1:

ArrayResize(PeakDN, k+1);     // Resize the trough array according to the number of troughs identified
ArrayResize(TimeDN, k+1);     // Resize the trough time array according to the number of troughs

und dann schreiben wir die Daten aus dem Array der Indikator-Werte in diese Arrays und erhöhen den Tiefs-Array Index um Eins:

PeakDN[k]=TempIND[i];         // Write the trough value into the trough array...
TimeDN[k]=iTime(sy,tf,i);     // ...and the time array
k++;                          // Increase the trough array index

Jetzt enthalten das Tiefs- und das Tief-Zeitpunkt-Array, PeakDN und TimeDN, die erforderlichen Daten der identifizierten Extremwerte und der Tiefs-Array Index zeigt auf die nächste Zelle dieses Arrays. Der nächste Schleifendurchlauf folgt entsprechend dem gleichen Muster bei der Identifizierung des nächsten Extremum und seine Daten werden in die neue Zelle des Tiefs-Array geschrieben, auf die der Tiefs-Array Index (k) zeigt.

Hochs (obere Extrema) werden n einer ganz ähnlichen Wiese identifiziert, mit der Ausnahme, dass der erste, zweite und der Balken auf die entgegengesetzte Weise verglichen werden: das 'weniger-als' und das 'größer-als' Zeichen sollten ausgetauscht werden. In dieser Phase kann unsere Funktion die A/D Indikator-Daten lesen, das Array mit diesen Daten füllen und Extrema in ihm identifizieren, sowie Werte und Positionen von Extrema in den vier vorgesehenen Arrays speichern.

Hier können wir anfangen Trendlinien in dem Indikator-Chart anzuzeigen, gezeichnet auf Grundlage der identifizierten Extrema.

Lassen Sie und zuerst "globale" Trendlinien in dem Indikator-Chart anzeigen. Um diese durch die Verbindung von Chart-Extrema zu zeichnen, müssen wir mit der Identifikation von zwei Extrema beginnen - das Extremum mit dem kleinsten Wert im gesamten Tiefs-Array und das Extremum auf das wir als Haupt-Extremum verweisen. Das Haupt-Extremum wird sich in unmittelbarer Nähe zum Anfang des Anfangs des Tiefs-Array befinden und strikt auf 2 gesetzt werden. Somit werden die ersten zwei Extrema am nächsten von allen zu dem aktuellen Balken in dem Indikator-Chart nicht berücksichtigt (um eine gewisse Freiheit für die Kursbewegung zu erhalten, ohne auf geringe auftretende Veränderungen reagieren zu müssen).

Jetzt, da das Haupt-Extremum identifiziert ist, müssen wir das Extremum mit dem kleinsten Wert ermitteln, das sich in dem Array links von dem Haupt-Extremum befindet. Dieses tiefste Extremum in dem Indikator-Chart wird als Ausgangspunkt für die Identifizierung des zweiten Extremum dienen. /p>

Das Zeichnen einer Trendlinie auf eine solche Weise, dass sei das tiefste Extremum und das Haupt-Extremum verbindet, wird sehr wahrscheinlich zu der Situation führen, in der die Trendlinie durch die Extrema höher gekreuzt wird, als das tiefste identifizierte Extremum. Und es bedeutet, dass der Anfang der Trendlinie in dieser Situation zu dem Extremum verschoben werden muss, dass die Trendlinie kreuzt. Die folgenden Abbildungen sind eine deutliche Darstellung eines solchen Falles:

Es ist zu erkennen, dass die von dem tiefsten Extremum ausgehende Linie durch höhere Extrema gekreuzt wird, während sie bis zu dem Haupt-Extremum reicht. Aus diesem Grund ist es in dieser Situation erforderlich das Array sorgfältig zu analysieren und das zu identifizieren, das, wenn es als Ausgangspunkt der Trendlinie definiert wird, nicht zu der Situation führt, in der die Trendlinie durch nachfolgende Extrema gekreuzt wird.

Das dem tiefsten Extremum folgende Extremum ist auch nicht geeignet - sollte die Trendlinie von diesem Punkt ausgehen, würde sie weiter von höheren Extrema gekreuzt:

Ich habe weitere Versuche hier ausgelassen, und bin direkt mit dem "richtigen" identifizierten Extremum fortgefahren:

Jetzt ist es an der Zeit alles in einen Code zu schreiben. Wir verwenden untere Extrema zum Zeichnen der Aufwärtstrend-Linie und obere Extrema zum Zeichnen der Abwärtstrend-Linie.

Lassen Sie uns ein Beispiel des Zeichnens einer Aufwärtstrend-Linie durch untere Extrema prüfen.



4. Identifizierung von Zwei Extrema in dem Array der A/D Indikator Extrema zum Zeichnen einer Trendlinie

Die Identifizierung von zwei benötigten Extrema erfordert die Verfügbarkeit von internen Variablen der Funktion zur Speicherung und Übergabe der Werte der unteren und oberen Minimum und Haupt-Extrema, zur Speicherung und Übergabe der Zeit der unteren und oberen Minimum und Haupt-Extrema und eine Variable für die Indizierung der Arrays.

Wir fügen Sie zu unseren Funktion-Variablen hinzu (um später nicht zu diesem Punkt zurück zu kommen, fügen wir die erforderlichen Funktion-Variablen direkt hinzu, um sie sofort zu starten):

// ------------------------------------------------------------------------------------------                     
// -------------------------------- Function variables ----------------------------
// ------------------------------------------------------------------------------------------                     

   double   PeakUP[], PeakDN[],        // Declare peak/trough arrays
            yUP, yDN, yGUP, yGDN,      // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar
            yUP2, yDN2, yGUP2, yGDN2,  // Coordinates of the crossing point between the trend line and indicator UP and DN on the 2nd bar
            CheckCross,
            PreLastVarDN,LastVarDN,    // Values of the last and last but one lower extrema
            PreLastVarUP,LastVarUP,    // Values of the last and last but one upper extrema
            PivotPeakDN,PivotPeakUP,
            LowestPeakDN,HighestPeakDN,// Values of the lower minimum and maximum 
            LowestPeakUP,HighestPeakUP,// Values of the upper minimum and maximum

   datetime TimeDN[], TimeUP[],        // Arrays for storing bars of extrema
            PreLastTimeDN, LastTimeDN, // Time of the last and last but one lower extrema
            PreLastTimeUP, LastTimeUP, // Time of the last and last but one upper extrema
            PivotBarDN, PivotTimeDN,   // Bar and time of the lower main extremum
            PivotBarUP, PivotTimeUP,   // Bar and time of the upper main extremum
            LowestBarDN, LowestTimeDN, // Bar and time of the lower minimum
            HighestBarUP, HighestTimeUP;// Bar and time of the upper maximum
   int      i, kup, kdn, pbar=2, 
            m, l, t, asize, Index,      // "Internal" variables
            WinID=WindowFind("A/D");    // AD window number
   bool     CrossDN = false,            // Flag indicating the downward crossover of the local trend
            CrossUP = false,            // Flag indicating the upward crossover of the local trend
            CrossGDN = false,           // Flag indicating the downward crossover of the global trend
            CrossGUP = false;           // Flag indicating the upward crossover of the global trend
   
   double   pt=MarketInfo(Symbol(),MODE_POINT); // Point size in the quote currency

und fügen einen Code für die Identifizierung unterer Extrema hinzu:

//====================================================================================================
// --------------------------- Identification of lower DN minimum and maximum -------------  
//====================================================================================================
          
  PivotTimeDN = TimeDN[pbar];                     // Time of the main extremum
  PivotBarDN  = iBarShift(sy,tf,TimeDN[pbar]);    // Bar of the main extremum
  PivotPeakDN = PeakDN[pbar];                     // Value of the main extremum
  LowestPeakDN  = ArrayMin(PeakDN);               // Get the lower minimum value
  Index = ArraySearchDouble(PeakDN, LowestPeakDN);// Get the index of the lower minimum in the array
  LowestBarDN =iBarShift(sy,tf,TimeDN[Index]);    // Get the bar of the lower minimum
  LowestTimeDN = TimeDN[Index];                   // Get the time of the lower minimum

Der Balken des Haupt-Extremum wird strikt in der Variable pbar festgelegt und die Zeit des Haupt-Extremum wird daher von dem Zeit-Array erhalten, durch den in dieser Variable enthaltenen Index.

PivotTimeDN = TimeDN[pbar];     // Time of the main extremum

Der Balken des Haupt-Extremum wird aus dem Zeit-Array erhalten und sein Wert wird berechnet mit der Standard-Funktion int iBarShift( string symbol, int timeframe, datetime time, bool exact=false).

Es wird später kurz beschrieben, wenn wir die Identifizierung des Minimum in dem Array der Extrema betrachten.

Der Wert des Haupt-Extremum wird dem Array der Werte empfangen mit Angabe des erforderliches Wertes mit der Variable pbar (die den Haupt-Extremum Index enthält):

PivotPeakDN = PeakDN[pbar];     // Value of the main extremum

Um das untere Minimum zu identifizieren, können Sie die Standardfunktion int ArrayMinimum (double array[], int count=WHOLE_ARRAY, int start=0) verwenden, die das ist, was wir verwenden nur werden zur Anordnung der Berechnung bei dem Aufruf einer benutzerdefinierten Funktion. Diese Funktion wird das Ergebnis der Extremum-Identifikation zurückgeben..

Um das Rad nicht neu zu erfinden, werden wir das Minimum-Element in dem Array werden wir eine funktionsfertige verwenden, freundlich für die öffentliche Verwendung bereitgestellt durch Igor Kim in seinem Forum-Thread. Sie passt perfekt zu unseren Bedürfnissen und kann den relevanten Fehlerbericht zu der Protokolldatei senden, sollte das Array an sie übergeben, dass es leer ist (dies wird praktisch beim Debuggen):

//+----------------------------------------------------------------------------+
//|  Author    : Igor B. Kim aka KimIV,  http://www.kimiv.ru                   |
//+----------------------------------------------------------------------------+
//|  Version   : 17.05.2008                                                    |
//|  Description : Returns the value of the minimum element in the array.      |
//+----------------------------------------------------------------------------+
//|  Parameters:                                                               |
//|    x - array of values of numerical series                                 |
//+----------------------------------------------------------------------------+
double ArrayMin(double& x[]) {
  if (ArraySize(x)>0) return(x[ArrayMinimum(x)]);
  else {
    Print("ArrayMin(): Array empty!");
    return(0);
  }
}

Wie Sie sehen, ist nichts wirklich Schwieriges dabei - Es ist die gleiche Standardfunktion ArrayMinimum(), die vorher das Array nach erforderlichen Daten geprüft hat. Eine ähnliche aus der gleichen Quelle erhaltene Funktion, wird verwendet um den Index des Minimum-Elements durch seinen Wert zu erhalten:

//+----------------------------------------------------------------------------+
//|  Author    : Igor B. Kim aka KimIV,  http://www.kimiv.ru                   |
//+----------------------------------------------------------------------------+
//|  Version   : 01.09.2005                                                    |
//|  Description : Finds an array element by its value                         |
//|             and returns the index of the found element or -1.              |
//+----------------------------------------------------------------------------+
//|  Parameters:                                                               |
//|    m - array of elements                                                   |
//|    e - value of the element                                                |
//+----------------------------------------------------------------------------+
int ArraySearchDouble(double& m[], double e) {
  for (int i=0; i<ArraySize(m); i++) {
    if (m[i]==e) return(i);
  }
  return(-1);
}

Hier schleifen wir das gesamte Array durch, nach dem an die Funktion übergebenen Wert suchend. Wenn der an die Funktion übergebene Wert mit dem Array-Element übereinstimmt, gibt die Funktion den Wert der Variable i zurück, die bei Übereinstimmung gleich zu dem gesuchten Wert in dem Array ist. Wenn der gesuchte Wert nicht gefunden wird, wird die Funktion -1 zurückgeben, was zur Fehlerprüfung in Ihrem Programm verwendet werden kann.

Jetzt müssen wir den Balken im Chart, der dem ermittelten Extremum entspricht. Zu diesem Zweck werden wir die Daten aus dem vorher gefüllten Array TimeDN[] und den Index (Index) des erforderlichen Extremum, das wie bereits erhalten haben:

// Get the bar of the lower minimum
LowestBarDN =iBarShift(sy,tf,TimeDN[Index]);

Um dies zu tun verwenden wir die Standardfunktion int iBarShift( string symbol, int timeframe, datetime time, bool exact=false)

Ermitteln des Balkens nach Zeit. Die Funktion gibt die Verschiebung des Balken zurück, der die angegebene Zeit hat. Wenn kein Balken mit der angegebenen Zeit vorhanden ist (eine "Lücke" (Gap) in der Historie), gibt die Funktion, abhängig von dem genauen Parameter, -1 zurück oder die Verschiebung des naheliegendsten Balken.

Hier ist der erste an die Funktion zu übergebende Parameter das aktuell in der Variable sy gespeicherte Symbol des Charts, der zweite zu übergebende Parameter ist der aktuell in der Variable tf Zeitrahmen und die Zeit datetime Zeit übergeben an iBarShift() die in dem Zeit-Array TimeDN[] gespeichert ist und indiziert durch den während des vorherigen Schritts erhaltenen Wert der Variable Index.

Wir belassen den Standardwert von bool exact, weil das Übergeben von true, wenn der Balken fehlt (eine "Lücke" in der Historie) wird dies die Funktion veranlassen -1 zurückzugeben, was anschließend in den Berechnungen verwendet wird und zu Fehlern führt. Der Standardwert false , sollte der Balken in der Historie fehlen, wird dazu führen, dass die Funktion den Wert des naheliegendsten Balken zurückgibt, der weder gut, noch besser als ein absolut falscher Wert ist.

Um jetzt ein vollständiges Bild des Ganzen und weiterer Berechnungen zu erhalten, werden wir außerdem die Zeit des Balken wie weiter oben beschrieben speichern. Dies ist sehr einfach: Wir haben die Zeit in dem TimeDN[] Array gespeichert. Und werden sie aus diesem Array durch Verwendung des Index erhalten:

// Get the time of the lower minimum
LowestTimeDN = TimeDN[Index];

So haben wir das Haupt-Extremum und das Minimum identifiziert. Jetzt müssen wir feststellen, ob die die zwei ermittelten Chartpunkte verbindende Trendlinie von anderen Extrema, zwischen den zwei identifizierten Extrema liegenden, gekreuzt wird (wie weiter oben bereits erwähnt):

Solche Kreuzungen werden mit einer virtuell gezeichneten Linie durch das Minimum in Richtung Maximum ermittelt. Wir werden das Array der Extrema Durchschleifen, von dem Minimum in Richtung des Haupt-Extremum, und sobald der Kreuzungspunkt zwischen dem Extremum und der Linie ermittelt ist, wird er als der Ausgangspunkt der Linie behandelt und wir werden das Array wieder Durchschleifen, beginnend bei dem gerade ermittelten Extremum zu dem Haupt-Extremum.

Das die Linie kreuzende Extrema wird durchlaufen bis keines von ihnen mehr übrig ist. Dies bedeutet, dass wir das erforderliche Extremum ermittelt haben und, dass die folgenden Extrema auf dem Weg zu dem Haupt-Extremum die Trendlinie nicht kreuzen.

if (LowestBarDN>PivotBarDN)                           // If the minimum is to the right of the first extremum ... 
                                                      // ... (being in 0 cell of the trough time array)
for (m=Index-1; m>pbar; m--)                          // Loop from the extremum following the minimum to the first extremum
  {
// --------- Draw a virtual trend line and check it for crossovers with other extrema -----                                  
  CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]),
                            PeakDN[m+1],                      // First coordinate of the line
                            PivotBarDN, PivotPeakDN,          // Second coordinate of the line
                            iBarShift(sy,tf,TimeDN[m],false));// Crossing point associated with the next extremum
      if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross)     // If the extremum lies below the line
         {
            if (PeakDN[m]<PeakDN[pbar])                       // if this extremum is lower than the main extremum
               {
                  LowestBarDN =iBarShift(sy,tf,TimeDN[m]);    // Then this is the one we need to start from...
                  LowestPeakDN  = PeakDN[m];                  // New coordinates of the next extremum
                  LowestTimeDN = TimeDN[m];                             
               }
         }                       
   }

Hier haben wir eine Schleife von dem auf das Minimum folgenden Extremum zu dem ersten Extremum in dem Array der Extrema angelegt. Dies wird gemacht, weil das Minimum der Ausgangspunkt sein wird, wenn die Linie gezeichnet wird, während die Prüfung nach Überkreuzungen von dem nächsten Extremum aus gestartet wird.

Die virtuelle Linie wird mit der Geradengleichung-Funktion gezogen, die den Wert von Y für X an dem Punkt der Überkreuzung der geraden Linie berechnet:

double EquationDirect(double x1, double y1, double x2, double y2, double x) {
if (x2==x1) return(y1);
return((y2-y1)/(x2-x1)*(x-x1)+y1);

x1 und y1 sind die Koordinaten des ersten Punkts, x2 und y2 sind die Koordinaten des zweiten Punkts, x ist der Wert, für den y berechnet wird. Diese Funktion wurde entnommen aus I. Kim's Forum Thread.

Also:

CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]), PeakDN[m+1], // First coordinate of the line
                          PivotBarDN, PivotPeakDN,                   // Second coordinate of the line                                  
                          iBarShift(sy,tf,TimeDN[m],false));         // Crossing point associated with the next extremum

Geben Sie den Balken des ersten Minimum x-Koordinate ein und den Wert des Minimum als die y-Koordinate des ersten Punkts in den Funktion-Parametern zum Übergeben. Der Balken und der Wert des Haupt-Extremum werden als der zweite virtuelle Linienpunkt verwendet.

Der letzte an die Funktion zu übergebende Wert ist die Balkennummer, die in die Schliefe zu der Funktion zur Berechnung des Wertes der y-Koordinate und Vergleichen der resultierenden Werte mit dem Wert des an dem gegebenen Punkt befindlichen Extremum.

if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross)  // If the extremum lies below the line
   {
    if (PeakDN[m]<PeakDN[pbar])                      // if this extremum is lower than the main extremum
       {
         LowestBarDN  =iBarShift(sy,tf,TimeDN[m]);   // Then this is the one we need to start from...
         LowestPeakDN = PeakDN[m];                   // New coordinates of the next extremum
         LowestTimeDN = TimeDN[m];                             
        }
   }            

Wenn demnach der durch die Funktion zurückgegebene Wert größer als der Wert an dem angegebenen Punkt ist, kreuzt das Extremum die virtuelle Linie und die Linie sollte von diesem Punkt ausgehen. Speichern Sie die neuen Koordinatenwerte und fahren Sie fort mit dem nächsten Schleifendurchlauf, um eine Überkreuzung mit dem nächsten Extremum zu identifizieren.

Sobald wir das folgende Extremum identifiziert haben, das die Linie nicht kreuzt, wird es zum Zeichnen der Trendlinie verwendet. Alles, was wir wissen müssen ist, ob der Wert dieses Extremum weniger ist als der des Haupt-Extremum und ob er links von dem Haupt-Extremum liegt, was bedeutet, dass die von ihm aus gezogene Linie Richtung Haupt-Extremum in die richtige Richtung verläuft.

if (LowestBarDN>PivotBarDN && LowestPeakDN<PivotPeakDN)// If the minimum is to the left of and below the main extremum

Und es bedeutet, dass wir nun alle benötigten Daten zum Zeichnen einer Aufwärts-Trendlinie in dem A/D Chart haben.



5. Zeichnen von Trendlinien in dem A/D Chart

Um die Anwesenheit des A/D Indikatorfensters im Fenster des Haupt-Charts zu identifizieren, führen wir eine weitere Variable WinID ein, welche die A/D Chart Unterfenster-Nummer speichern wird:

WinID=WindowFind("A/D");               // AD window number

Hier gibt die Standardfunktion WindowFind("name"); die Nummer des Chart-Unterfensters zurück, das den Indikator mit dem angegebenen Namen name enthält, wenn er gefunden wurde, andernfalls wird -1 zurückgegeben. Es gibt im Grunde keine Notwendigkeit die Trendlinie in das A/D Chart zu zeichnen, da sie für Berechnungen nicht benötigt wird. Wir zeichnen sie allerdings zur Verdeutlichung und um sicher zu stellen, dass alles korrekt ist:

if (WinID>0)
   {
    if (ObjectFind("Trend_GLine_DN")<0)
    ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN);
    ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime);
    ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN);
    ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN);
    ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN);
    ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN);
   }

Hier prüfen wir zuerst ob das A/D Indikator-Fenster im Hauptfenster vorhanden ist. Wenn es nicht dort ist, ist dort nichts zum Zeichnen:

if (WinID>0)

Es ist einfach: Wenn der Indikator nicht an an das Fenster angehangen ist, wird die WindowFind() Funktion -1 zurückgeben. Das Haupt-Chartfenster, d.h. das Chart selbst, hat immer Null-Wert, und aus diesem Grund prüfen ob der Wert "größer als Null" ist. Da es, abgesehen von dem A/D Chart, andere Indikatoren in dem Chart-Fenster geben kann, können wir nicht sicher wissen, welche von uns benötigte Nummer dem A/D zugewiesen wird und folglich prüfen wir nicht die Nummer selbst, sondern vielmehr ob der Wert größer als Null ist.

Ferner... Wenn die Fenster-Kennung (WinID) größer als Null ist, wird das A/D Chart in dem Fenster als vorhanden betrachtet und wir können mit dem Zeichnen fortfahren. Dann prüfen wir ob die Trendlinie bereits in das Chart gezeichnet wurde mit dem Namen:

if (ObjectFind("Trend_GLine_DN")<0)

und wenn sie noch nicht dort ist, (die ObjectFind() Funktion hat -1 zurückgegeben, andernfalls wird der A/D Fenster Index zurückgegeben), können wir anfangen zu zeichnen. Zu diesem Zweck verwenden wir die Funktion:

ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN);

bool ObjectCreate(string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0) erzeugt ein Objekt mit dem angegebenen Namen, Typ und Anfangskoordinaten in einem angegebenen Chart-Unterfenster. Die Anzahl der mit dem Objekt Koordinaten kann 1-3 sein, abhängig von dem Typ. Nach erfolgreichem Abschluss des Objekts gibt die Funktion TRUE zurück, andernfalls FALSE.

Lassen Sie es uns im Detail prüfen:

  • string name nimmt den Wert des Namens des zu erstellen erforderlichen Objekts - in unserem Fall, "Trend_GLine_DN" (globale Trendlinie verbindet untere Extrema), der nächste an die Funktion übergebene Parameter ist int type, d.h. der Objekt-Typ - in unserem Fall ist es vernünftigerweise OBJ_TREND...
  • int window ist die A/D Indikatorfenster-Kennung, gespeichert in der Variable WinIND...

Weitere an die Funktion übergebene Parameter sind Koordinaten der zwei Punkte die zum Zeichnen der Trendlinie verwendet werden:

  • datetime time1 ist die erste Koordinate des Minimum, geseichert in der Variable LowestTDN,
  • double price1 ist die erste Koordinate des Minimum, gespeichert in der Variable LowestPeakDN,
  • datetime time2 ist die zweite Koordinate des Minimum, gespeichert in der Variable HighestTDN,
  • double price2 ist die zweite Koordinate des Minimum, gespeichert in der Variable HighestPeakDN.

Andere Parameter der Funktion ObjectCreate() werden bei dem Zeichnen der Trendlinie nicht verwendet.

Also, in dieser Phase wurde das Trendlinie-Objekt erstellt. Jetzt müssen wir einige seiner Parameter ändern. Insbesondere die Linienfarbe und Linien-Koordinaten müssen jedes Mal modifiziert werden, wenn neue Extrema wechseln und neue Koordinaten zum Zeichnen der Trendlinie verwendet werden:

ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime);

bool ObjectSet( string name, int prop_id, double value) Änderungen der Werte der angegebenen Objekteigenschaften. Bei Erfolg gibt die Funktion TRUE zurück, andernfalls FALSE. Diese Codezeile bestimmt die Farbe der Aufwärtstrend-Linie durch die Zuweisung der Standard-Konstante Lime auns dem Satz der Web-Farben.

Die an die Funktion übergebenen Parameter sind:

  • string name - der Name des Objekts dessen Eigenschaften modifiziert werden müssen - in unserem Fall,, "Trend_GLine_DN",
  • int prop_id - Kennung der Objekteigenschaften die geändert werden müssen. Zuerst ändern wir die Farbe, deshalb ist die Kennung OBJPROP_COLOR,
  • double value - neuer Wert der angegebenen Objekteigenschaft. . Das ist es, wohin die Farb-Konstante Lime übergeben wird.

Ich glaube, die Änderungen weiterer Objekteigenschaften sollte keine Schwierigkeit darstellen - es ist ähnlich wie das obige Beispiel, einschließlich der Änderung in der Objektfarbe:

ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN);
ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN);
ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN);
ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN);

Zuerst ändern wir die erste Zeit-Koordinate des Extremum (OBJPROP_TIME1), gefolgt von der Änderung der ersten Wert-Koordinate (OBJPROP_PRICE1), dann ändern wir die zweite Zeit-Koordinate des Extremums (OBJPROP_TIME2) und die zweite Wert-Koordinate (OBJPROP_PRICE1). Alle Koordinatenwerte werden in die Variablen LowestTimeDN, LowestPeakDN, PivotTimeDN und PivotPeakDN geschrieben.

Unsere Trendlinie wird in das A/D Indikator-Chart gezeichnet (vorbehaltlich des Vorhandenseins des Indikator-Fensters selbst). Jetzt müssen wir den Kreuzungspunkt zwischen den Linien in dem A/D Chart und der mit mit dem Indikator-Chart Extremum gezeichneten Trendlinie ermitteln. Beim Nachdenken über diese Aufgabe kam ich zu dem Schluss, dass es einfacher ist (für mich) den Kreuzungspunkt mit einer Geradengleichung zu berechnen.



6. Identifizieren des Kreuzungspunkts zwischen der A/D-Chart Linie und und der über die Indikator-Chart Extrema Gezeichnete Trendlinie

Mit zwei Punkten, durch welche die die Trendlinie gezeichnet wird, verwenden wir die Geradengleichung zur Berechnung einer imaginären Linie, die auf den ersten Balken der A/D Indikator-Linie projiziert wird (wir arbeiten mit dem ersten Balken um die Anzahl der Fehlalarme zu reduzieren).

Der mit der Geradengleichung und auf den ersten Balken des Indikators projizierte Punkt, wird der Punkt sein, an dem wir auf Überkreuzungen prüfen. Darüber hinaus müssen wir nur diese berechneten Punkte in dem A/D Chart markieren und überprüfen allein die Tatsache, ob sie durch die Indikator-Linie aufwärts oder abwärts gekreuzt werden.

Es ist ein weiterer Punkt zu berücksichtigen. Da wir den Kreuzungspunkt als solchen überprüfen werden, im Gegensatz zu höher oder niedriger, werden unsere Berechnungen außerdem den zweiten Balken des A/D Indikator-Chart benötigen. Wenn die Indikator-Linie höher ist als der Kreuzungspunkt auf dem ersten Balken und tiefer oder gleich zu dem Kreuzungspunkt des zweiten Balken, haben wir eine Aufwärts-Überkreuzung. Somit werden wir zwei Linien basierend auf den Extrema berechnen. Die erste Linie wird auf den ersten Balken des Indikators projiziert und die zweite Linie wird auf den zweiten Balken projiziert.

Imaginäre Kreuzungspunkte werden markiert, für eine bessere Visualisierung unserer Berechnungsergebnisse.

Als, fangen wir an...



7. Identifizierung und Markierung möglicher Kreuzungspunkte

Wir benötigen eine zusätzliche Variable zum Speichern der berechneten Werte möglicher Kreuzungspunkte.

Wir haben sie bereits zu der Linste der Funktionsvariablen hinzugefügt:

// ------------------------------------------------------------------------------------------                     
// -------------------------------- Function variables ----------------------------
// ------------------------------------------------------------------------------------------                     
   double   PeakUP[], PeakDN[],           // Declare peak/trough arrays
            yUP, yDN, yGUP, yGDN,         // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar
            yUP2, yDN2, yGUP2, yGDN2,     // Coordinates of the crossing point between the trend lines and indicator UP and DN on the 2nd bar
            LowestPeakDN,HighestPeakDN,   // Values of the lower minimum and maximum
            LowestPeakUP,HighestPeakUP;   // Values of the upper minimum and maximum
   datetime TimeDN[], TimeUP[],           // Arrays for storing bars of extrema
            LowestTimeDN, HighestTimeDN,  // Bar of the lower minimum and maximum
            LowestTimeUP, HighestTimeUP,  // Bar of the upper minimum and maximum
            LowestTDN, HighestTDN,        // Time of the lower minimum and maximum
            LowestTUP, HighestTUP;        // Time of the upper minimum and maximum
   int      i, k, Index,                  // "Internal" variables
            WinID=WindowFind("A/D");      // AD window number

Um mit der Geschichte voran zu kommen, möchte ich erwähnen, dass wir anstatt 4, 8 Variablen hinzugefügt haben, da weitere 4 Variablen für die Berechnung von "lokalen" Trendlinien und Kreuzungspunkte erforderlich sind. Wir verwenden yGUP, yGDN, yGUP2 und yGDN2 zum Speichern von Kreuzungen mit

  • der "globalen" Aufwärts-Trendlinie yGUP auf dem ersten Balken, yGUP2 - auf dem zweiten Balken und
  • der "globalen" Abwärts-Trendlinie yGDN auf dem ersten Balken, yGDN2 - auf dem zweiten Balken.

Verwenden wir noch einmal die Geradengleichung-Funktion, freundlicherweise bereitgestellt durch Igor Kim:

//+----------------------------------------------------------------------------+
//|  Author    : Igor B. Kim aka KimIV,  http://www.kimiv.ru                   |
//+----------------------------------------------------------------------------+
//|  Version   : 12.10.2007                                                    |
//|  Description : Straight line equation. Calculates the Y value for X        |
//|                at the point of crossing with the straight line.            |
//+----------------------------------------------------------------------------+
//|  Parameters:                                                               |
//|    x1,y1 - coordinates of the first point,                                 |
//|    x2,y2 - coordinates of the second point,                                |
//|    x     - value for which Y needs to be calculated                        |
//+----------------------------------------------------------------------------+
double EquationDirect(double x1, double y1, double x2, double y2, double x) 
{
  if (x2==x1) return(y1);
  return((y2-y1)/(x2-x1)*(x-x1)+y1);
}

5 Parameter werden an die Funktion als Startpunkte für die Berechnung übergeben: zwei für die Koordinaten der zwei Chart-Extrema und der letzte Parameter ist der Balken, auf dem der Kreuzungspunkt gefunden werden sollte:

yGDN =EquationDirect(LowestBarDN,LowestPeakDN,PivotBarDN,PivotPeakDN,iBarShift(sy,tf,Time[1],false));
yGDN2=EquationDirect(LowestBarDN,LowestPeakDN,PivotBarDN,PivotPeakDN,iBarShift(sy,tf,Time[2],false));

Hier werden die Koordinaten des Minimums als die Koordinaten des ersten Punkts verwendet: x1 = LowestTimeDN, y1 = LowestPeakDN, während die Koordinaten des Haupt-Extremums als die Koordinaten für den zweiten Punkt verwendet werden: x2 = PivotBarDN, y2 = PivotPeakDN.

Die Verschiebung des erforderlichen Balken wird als der Wert übergeben, für den die Gleichung berechnet wird: iBarShift(sy, tf, Time[1], false), wobei sy der symbolische Name des Währungsinstruments ist, tf ist der Zeitrahmen, Time[1] ist das Zeitreihen-Array mit der Öffnungszeit für jeden Balken in dem aktuellen Chart. Der Datum-Datentyp stellt sie Zeit in vergangenen Sekunden seit 00:00 Uhr am 1. Januar 1970 dar.

Die verbleibenden Parameter dieser Funktion wurden bereits besprochen. Ich möchte darauf hinweisen, dass wenn unsere Funktion mit einem Multi-Währungs-EA verwendet wird, sollten wir anstatt Time[1] , die Standardfunktion iTime() verwenden (die das ist, was wir in der abschließenden Variante der Funktion umsetzen werden), die explizit Werte der Währungsinstrumente erhält und den verwendeten Zeitrahmen, wobei Time[] nur mit dem aktuellen Chart funktioniert.

Wir haben die Kreuzungsebenen berechnet und in den Variablen gespeichert. Zur Verdeutlichung können diese nun in dem A/D Indikator-Chart markiert werden:

if (WinID>0)
     {
         if (ObjectFind("PointGDN"+Time[1])<0)
         ObjectCreate("PointGDN"+Time[1],OBJ_ARROW,WinID,Time[1],yGDN);
         ObjectSet("PointGDN"+Time[1],OBJPROP_ARROWCODE,4);
         ObjectSet("PointGDN"+Time[1],OBJPROP_COLOR,Lime);
         ObjectSet("PointGDN"+Time[1],OBJPROP_TIME1,Time[1]);
         ObjectSet("PointGDN"+Time[1],OBJPROP_PRICE1,yGDN);
     }

Dies ist ähnlich wie das, was wir beim Zeichnen der Trendlinie gemacht haben. Werfen wir einen Blick auf ein paar kleine Unterschiede.

Der erste Unterschied ist in dem Objektnamen:

ObjectCreate("PointGDN"+Time[1],OBJ_ARROW,WinID,Time[1],yGDN);

Da wir kein einzelnes Objekt erzeugen, sondern vielmehr einen Satz an Objekten bei jedem neuen Balken, müssen die Namen aller Objekte einzigartig sein. Zu diesem Zweck wird die Zeit, zu der das Objekt erzeugt wurde, dem Objektnamen hinzugefügt:

"PointGDN"+Time[1]

Der zweite Unterschied ist die Kennung des grafischen Objekttyps. Während wir für das Zeichnen der Trendlinie OBJ_TREND verwendet haben, verwenden wir jetzt OBJ_ARROW, was ermöglicht Pfeile zu zeichnen (Zeichen). Darüber hinaus ist WinID die Nummer des Unterfenster, in dem das Objekt gezeichnet wird - in unserem Fall ist es die A/D Fensternummer, und dort sind zwei Koordinaten - die Zeit-Koordinate Time[1] ist die Zeit des ersten Balken und die Kurs-Koordinate yGDN ist der Kreuzungspunkt zwischen der Trendlinie und dem ersten Balken, wie berechnet.

Die Objekteigenschafen werden wie folgt festgelegt:

ObjectSet("PointGDN"+Time[1],OBJPROP_ARROWCODE,4);

Die Kennung der Objekteigenschaften OBJPROP_ARROWCODE legt den Objektcode fest und der kann eines der Wingdings Symbole sein oder einer der vordefinierten Pfeil-Codes. Um das Objekt genau auf das berechnete Niveau zu setzen, gibt es spezielle Pfeil-Codes die genau auf Kurs und Zeit zeigen. Wir verwenden den Bindestrich (-), sein Code ist die 4. die verbleibenden Zeilen bestimmen die Linienfarbe und ihre ständig verändernden Koordinaten auf jedem Balken.

ObjectSet("PointGDN"+Time[1],OBJPROP_COLOR,Lime);
ObjectSet("PointGDN"+Time[1],OBJPROP_TIME1,Time[1]);
ObjectSet("PointGDN"+Time[1],OBJPROP_PRICE1,yGDN);

Jetzt haben wir die möglichen Kreuzungspunkte zwischen der Indikator-Linie und der "globalen" Aufwärts-Trendlinie, basierend auf den Extrema gezeichnet. Es sind diese Punkte, die zur Überprüfung von Überkreuzungen benötigt werden. Wenn eine Überkreuzung ermittelt wird, wird die Signalanzeige, der Pfeil nach unten - in das Hauptchart gesetzt, was das ist, was wir übrigens jetzt machen werden.



8. Prüfung nach Überkreuzungen der A/D-Linie und die Berechneten Kreuzungspunkte und Einstellung der Signalanzeige

Um zu überprüfen, ob es eine Überkreuzung der A/D Indikator-Linie gibt, vergleichen wir einfach deren Werte auf größer aal/geringer als, und wenn der Wert der Indikator-Linie auf dem gegebenen Balken weniger ist als der Kreuzungspunkt-Wert, während der Indikator-Wert auf dem vorherigen Balken größer als der Kreuzungspunkt-Wert ist, dann haben wir es mit einer Abwärts-Überkreuzung zu tun:

if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && 
     NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8))
    {
        CrossGDN = true;           // Set the flag indicating the downward crossover
        CrossGUP = false;          // Remove the flag indicating the upward crossover
                                    
        if (ObjectFind("ArrowGDN"+Time[1])<0)
        ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt);
        ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242);
        ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed);
        ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1);
        ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]);
        ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt);
    }

Um verglichen zu werden, müssen die double Typ Datenwerte auf die erforderliche Genauigkeit normiert werden mit der Standardfunktion double NormalizeDouble(double value, int digits). Fließkommazahlen werden auf die erforderliche Genauigkeit gerundet.

Die berechneten StopLoss und TakeProfit Werte, sowie die Eröffnungskurse von Pending Ordern, sollten auf die Genauigkeit normiert werden, wie die in der vordefinierten Variable Digits festgelegt ist:

if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && 
    NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8))

was genau das ist, was wir bei dem Vergleich der Indikator-Linie Werte mit den berechneten Kreuzungspunkt-Werten gemacht haben. Wenn diese Bedingung erfüllt ist, erhalten die Variablen des bool Typ CrossGDN und CrossGUP die Werte true und false zugewiesen.

Der nächste Block des Codes setzt den Abwärts-Pfeil im Haupt-Chartfenster des Terminals:

if (ObjectFind("ArrowGDN"+Time[1])<0)
  ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt);
  ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242);
  ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed);
  ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1);
  ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]);
  ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt);

Ich glaube, benötigt keine weiteren Klarstellungen, da wir bereits besprochen haben, wie Markierungen und Linien im Fenster des Terminals gesetzt werden können. Der einzige früher nicht berücksichtigte Unterschied ist die Spezifikation des Haupt-Chartfensters des Terminals. Die Nummer dieses Fensters ist immer 0, dadurch ermöglichen wir durch Einstellen der Kennung 0 als Fensterkennung in der folgenden Zeile

ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+5*pt);

Pfeile in das Hauptfenster des Terminals zu setzen.

Ein paar weitere Dinge, die zu beachten sind: Der maximale Kurswert des Balken oben, der als die y-Koordinate des Abwärts-Pfeils (sein Code ist 242) und die Einrückung wird auf 5 Punkte festgelegt:

iHigh(sy,tf,1)+5*pt

double iHigh( string symbol, int timeframe, int shift) gibt den maximalen durch den Parameter shift angegebenen Kurswert des Balkens aus dem entsprechenden Chart wieder (Symbol, Zeitrahmen).

Der Wert von pt wird von Zeile unmittelbar nach der Deklaration der Funktion-Variablen empfangen:

double pt=MarketInfo(Symbol(),MODE_POINT);

Dies ist eine Standardfunktion double MarketInfo( string symbol, int type). Sie gibt verschiedene Informationen über in dem "Market Watch" Fenster gelistete Finanzinstrumente wieder. Die Funktionsabfrage Kennung MODE_POINT gibt die Punktgröße in dem Kurs der Währung zurück. Für das aktuelle Instrument ist es in der vordefinierten Variable Point gespeichert. Die Zeile:

ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,1);

bestimmt die Größe des angezeigten Pfeils = 1.



9. Prüfen der Überkreuzung der Aufwärtstrend-Linie in der Richtung der Kursbewegung des Währungspaares

Eine Abwärts-Überkreuzung einer Aufwärtstrend-Linie kann signalisieren, dass die Kursbewegung des Währungspaares sich in Kürze ändern kann, während eine Aufwärts-Überkreuzung der gleichen Aufwärtstrend-Linie andeuten kann, dass der Kurs sich weiter in die gleiche Richtung bewegen wird. Deshalb, um auf der sicheren Seite zu sein, werden wir auch diese Art der Überkreuzung prüfen, wobei wir gleichzeitig ermöglichen die Prüfung zu deaktivieren, wenn notwendig.

Zu diese, Zweck ändern wir die erste Zeile der Funktion durch das Hinzufügen von zwei weiteren Parametern, die an die Funktion übergeben werden:

int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, bool local=false, bool add=false)

Wir haben hier zwei Variablen des bool Typs hinzugefügt: local und add. Die Variable local ist verantwortlich für die zur Anzeige der "lokalen" Trendlinie erforderlichen Berechnung, während die Variable add der Überprüfung auf einer Aufwärts-Überkreuzung der Aufwärtstrend-Linie und der Abwärts-Überkreuzung der Abwärtstrend-Linie dient. Standardmäßig sind diese Variablen auf false gesetzt, was bedeutet, wenn sie bei dem Funktionsaufruf ausgelassen werden, wird die Funktion nicht berechnen, nicht die lokale Trendlinie anzeigen, noch wird sie nach deren Überkreuzungen prüfen.

if (add)
    {
       if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGDN,8) && 
           NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGDN2,8))
           {
              CrossGUP = true;
              CrossGDN = false;        
              if (ObjectFind("ArrowGUP"+Time[1])<0)
              ObjectCreate("ArrowGUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1));
              ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241);
              ObjectSet("ArrowGUP"+Time[1],OBJPROP_COLOR,Lime);
              ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,0);
              ObjectSet("ArrowGUP"+Time[1],OBJPROP_TIME1,Time[1]);
              ObjectSet("ArrowGUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1));
           }
    }

In diesem Codeblock ist alles vertraut und es ist im Grunde nicht notwendig ihn genauer anzusehen. Was wir genau festlegen müssen, ist die Überprüfung der Variable add:

if (add)

ist absolut identisch mit diesem:

if (add==true)

und prüft, ob diese Variable auf "true" gesetzt ist. Wen sie es ist, werden alle in {} Klammern eingeschlossenen Zeilen nach der gegebenen Zeile ausgeführt. Es bleiben nur zwei Zeilen zu betrachten:

ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241);

Hier ist der letzte an die Funktion übergebene Parameter der Aufwärts-Pfeil Code - 241, und

ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,0);

wo der letzte an die Funktion übergebene Parameter die Größe des gezeichneten Pfeils bezeichnet. In unserem Fall werden wir einen kleinen Pfeil der Größe 0 anzeigen.

Der im Moment vorliegende Code ist voll funktionsfähig: er liest die A/D Indikatordaten und verwendet sie zum Füllen des Array, ermittelt die Indikator-Chart Extrema, zeichnet die Aufwärtstrend-Linie in das Chart, prüft nach Überkreuzungen dieser Linie und der A/D-Indikator-Linie, wird eine solche Überkreuzung ermittelt, platziert er Auf- oder Ab-Pfeile (abhängig von der Kreuzungsrichtung) in das Haupt-Chart des im des Währungsinstruments im Terminalfenster.

Im Folgenden ist eine ähnliche Prüfung, aber dieses Mal für die Abwärtstrend-Linie. Er ist für Ihre eigenständige Analyse angegeben:

//====================================================================================================
// -------------------- Identification of upper UP minimum and maximum --------------------            
//====================================================================================================
    PivotTimeUP = TimeUP[pbar];                           // Time of the main extremum
    PivotBarUP  = iBarShift(sy,tf,TimeUP[pbar]);          // Bar of the main extremum
    PivotPeakUP = PeakUP[pbar];                           // Value of the main extremum

    HighestPeakUP = ArrayMax(PeakUP);                     // Get the upper maximum value
    Index = ArraySearchDouble(PeakUP, HighestPeakUP);     // Get the index of the upper maximum in the array
    HighestBarUP =iBarShift(sy,tf,TimeUP[Index]);         // Get the bar of the upper maximum
    HighestTimeUP = TimeUP[Index];                        // Get the time of the upper maximum

    if (HighestBarUP>PivotBarUP)                          // If the maximum is to the left of the first extremum ... 
                                                          // ... (being in 0 cell of the peak time array)
   for (m=Index-1; m>pbar; m--)                           // Loop from the extremum following the maximum to the first extremum
      {
// --------- Draw a virtual trend line and check it for crossovers with other extrema ----------  
      CheckCross=EquationDirect(iBarShift(sy,tf,TimeUP[m+1]), 
                                PeakUP[m+1],                      // First coordinate of the line
                                PivotBarUP, PivotPeakUP,          // Second coordinate of the line
                                iBarShift(sy,tf,TimeUP[m],false));// Crossing point associated with the next extremum
      if (TempIND[iBarShift(sy,tf,TimeUP[m])]>CheckCross)         // If the extremum lies above the line
         {
            if (PeakUP[m]>PeakUP[pbar])                           // if this extremum is higher the main extremum
               {
                  HighestBarUP =iBarShift(sy,tf,TimeUP[m]);       // Then this is the one we need to start from...
                  HighestPeakUP = PeakUP[m];                      // New coordinates of the next extremum
                  HighestTimeUP = TimeUP[m];                             
               }
         }                       
   }

   if (HighestBarUP>PivotBarUP && HighestPeakUP>PivotPeakUP)      // If the maximum is to the left of the main extremum     
      {
// ---------------- Draw a downtrend line (UP - extrema) ---------------------                         
          if (WinID>0)
             {
                if (ObjectFind("Trend_Line_GUP")<0)
                ObjectCreate("Trend_Line_GUP",OBJ_TREND,WinID,HighestTimeUP,HighestPeakUP,PivotTimeUP,PivotPeakUP);
                ObjectSet("Trend_Line_GUP",OBJPROP_COLOR,OrangeRed);
                ObjectSet("Trend_Line_GUP",OBJPROP_TIME1,HighestTimeUP);
                ObjectSet("Trend_Line_GUP",OBJPROP_PRICE1,HighestPeakUP);
                ObjectSet("Trend_Line_GUP",OBJPROP_TIME2,PivotTimeUP);
                ObjectSet("Trend_Line_GUP",OBJPROP_PRICE2,PivotPeakUP);
             }        
//---------------- Calculation of levels of the downtrend (UP - extrema) ------------------            
    yGUP =EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,Time[1],false));
    yGUP2=EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,Time[2],false));

//---------------- Draw levels of the downtrend (UP - extrema) ------------------            
         if (WinID>0)
            {
               if (ObjectFind("PointGUP"+Time[1])<0)
               ObjectCreate("PointGUP"+Time[1],OBJ_ARROW,WinID,Time[1],yGUP);
               ObjectSet("PointGUP"+Time[1],OBJPROP_ARROWCODE,4);
               ObjectSet("PointGUP"+Time[1],OBJPROP_COLOR,OrangeRed);
               ObjectSet("PointGUP"+Time[1],OBJPROP_TIME1,Time[1]);
               ObjectSet("PointGUP"+Time[1],OBJPROP_PRICE1,yGUP);
            }
         
// --------------- Check for an upward crossover of the downtrend -----------------   
         if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGUP,8) && 
             NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGUP2,8))
               {
                  CrossGUP = true;                 // Set the flag indicating the upward crossover
                  CrossGDN = false;                // Remove the flag indicating the downward crossover
               
                  if (ObjectFind("ArrowGUP"+Time[1])<0)
                  ObjectCreate("ArrowGUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1));
                  ObjectSet("ArrowGUP"+Time[1],OBJPROP_ARROWCODE,241);
                  ObjectSet("ArrowGUP"+Time[1],OBJPROP_COLOR,Lime);
                  ObjectSet("ArrowGUP"+Time[1],OBJPROP_WIDTH,1);
                  ObjectSet("ArrowGUP"+Time[1],OBJPROP_TIME1,Time[1]);
                  ObjectSet("ArrowGUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1));
               }
            
// --------------- Check for a downward crossover of the downtrend -----------------   

         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGUP,8) && 
                   NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGUP2,8))
                  {
                     CrossGDN = true;
                     CrossGUP = false;
                              
                     if (ObjectFind("ArrowGDN"+Time[1])<0)
                     ObjectCreate("ArrowGDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+15*pt);
                     ObjectSet("ArrowGDN"+Time[1],OBJPROP_ARROWCODE,242);
                     ObjectSet("ArrowGDN"+Time[1],OBJPROP_COLOR,OrangeRed);
                     ObjectSet("ArrowGDN"+Time[1],OBJPROP_WIDTH,0);
                     ObjectSet("ArrowGDN"+Time[1],OBJPROP_TIME1,Time[1]);
                     ObjectSet("ArrowGDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt);
                  }
            }
      }  
                                  


10. Identifikation von "Lokalen" Extrema in dem A/D Chart, Zeichnen von Trendlinien Durch die Extrema und Prüfung nach Überkreuzungen

Also, es bleibt nur eine kleine Sache übrig: Das gleiche wie das Obige für "lokale" Extrema zu organisieren.

Die einzige Sache, in der sie sich von "globalen" Extrema unterscheiden, ist das Verfahren ihrer Identifikation. Sie werden mit den zwei äußersten rechten Extrema identifiziert, , und wenn (für einen Aufwärtstrend) das erste Extremum als höher erscheint als des zweite auf der linken Seite des ersten Extremum, nehmen wir an, dass eine lokale Trendlinie gezeichnet werden kann. Für einen Abwärtstrend muss das zweite ermittelte Extremum höher sein als und auf der linken Seite des ersten Extremum.

Da der Code absolut identisch zu dem oben bereits besprochenen (tatsächlich wird er fast identisch sein, abgesehen von ein paar Punkten), stelle ich ihn vollständig für eine lokale Aufwärtstrend-Linie bereit:

// --------------------------- Identification of two (local) outermost lower DN extrema ------------           
   if (local)
      {     
         asize=ArraySize(PeakDN);
         for (l=asize-1; l>=1; l--)
            {
               if (PeakDN[l]<PeakDN[l-1])
                  {
                     LastTimeDN     =TimeDN[l-1];
                     LastVarDN      =PeakDN[l-1];
                     PreLastTimeDN  =TimeDN[l];
                     PreLastVarDN   =PeakDN[l];
                  }
            }
// --------------- Draw a local uptrend line (DN - extrema) -----------------          
         if (WinID>0)
            {
               if (ObjectFind("Trend_Line_DN")<0)
               ObjectCreate("Trend_Line_DN",OBJ_TREND,WinID,TimeDN[1],PeakDN[1],TimeDN[0],PeakDN[0]);
               ObjectSet("Trend_Line_DN",OBJPROP_COLOR,MediumSeaGreen);
               ObjectSet("Trend_Line_DN",OBJPROP_TIME1,PreLastTimeDN);
               ObjectSet("Trend_Line_DN",OBJPROP_PRICE1,PreLastVarDN);
               ObjectSet("Trend_Line_DN",OBJPROP_TIME2,LastTimeDN);
               ObjectSet("Trend_Line_DN",OBJPROP_PRICE2,LastVarDN);
            }
//---------------- Calculation of levels of the local uptrend (DN - extrema) ----------------            
  yDN =EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, 
                      iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,Time[1],false));
  yDN2=EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, 
                      iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,Time[2],false));
               
//---------------- Draw levels of the local uptrend (DN - extrema) ----------------          
         if (WinID>0)
            {
               if (ObjectFind("PointDN"+Time[1])<0)
               ObjectCreate("PointDN"+Time[1],OBJ_ARROW,WinID,Time[1],yDN);
               ObjectSet("PointDN"+Time[1],OBJPROP_ARROWCODE,4);
               ObjectSet("PointDN"+Time[1],OBJPROP_COLOR,MediumSeaGreen);
               ObjectSet("PointDN"+Time[1],OBJPROP_TIME1,Time[1]);
               ObjectSet("PointDN"+Time[1],OBJPROP_PRICE1,yDN);
            }
// --------------- Check for a downward crossover of the local uptrend -----------------   
         if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yDN,8) && 
             NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yDN2,8))
               {
                  CrossDN = true;
                  CrossUP = false;
                  if (Arrow)                 // If the use of signal indicators is allowed
                     {
                        if (ObjectFind("ArrowDN"+Time[1])<0)
                        ObjectCreate("ArrowDN"+Time[1],OBJ_ARROW, 0 ,Time[1], iHigh(sy,tf,1)+15*pt);
                        ObjectSet("ArrowDN"+Time[1],OBJPROP_ARROWCODE,242);
                        ObjectSet("ArrowDN"+Time[1],OBJPROP_COLOR,Chocolate);
                        ObjectSet("ArrowDN"+Time[1],OBJPROP_WIDTH,1);
                        ObjectSet("ArrowDN"+Time[1],OBJPROP_TIME1,Time[1]);
                        ObjectSet("ArrowDN"+Time[1],OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt);
                     }
               }      
// --------------- Check for an upward crossover of the local uptrend -----------------   
         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yDN,8) && 
                   NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yDN2,8))
                  {
                     CrossUP = true;
                     CrossDN = false;
                     if (Arrow)                 // If the use of signal indicators is allowed
                        {
                           if (ObjectFind("ArrowUP"+Time[1])<0)
                           ObjectCreate("ArrowUP"+Time[1],OBJ_ARROW, 0 ,Time[1], iLow(sy,tf,1));
                           ObjectSet("ArrowUP"+Time[1],OBJPROP_ARROWCODE,241);
                           ObjectSet("ArrowUP"+Time[1],OBJPROP_COLOR,MediumSeaGreen);
                           ObjectSet("ArrowUP"+Time[1],OBJPROP_WIDTH,0);
                           ObjectSet("ArrowUP"+Time[1],OBJPROP_TIME1,Time[1]);
                           ObjectSet("ArrowUP"+Time[1],OBJPROP_PRICE1,iLow(sy,tf,1));
                        }
                  }                  
            }
      }            

Der einzige Unterschied ist hier die Identifizierung der Extrema, durch die die Trendlinie gezeichnet wird:

if (local)

Diese Linie prüft die Zeichnung der Trendlinie und die Berechnung von Überkreuzungen des A/D Chart wird ermöglicht.

Wir durchlaufen dann die Schleife auf der Suche nach den zwei äußersten Extrema:

asize=ArraySize(PeakDN);
  for (l=asize-1; l>=1; l--)
      {
       if (PeakDN[l]<PeakDN[l-1])
          {
            LastTimeDN     =TimeDN[l-1];
            LastVarDN      =PeakDN[l-1];
            PreLastTimeDN  =TimeDN[l];
            PreLastVarDN   =PeakDN[l];
          }
      }

und schreiben ihre Werte in die Variablen LastTimeDN, LastVarDN, PreLastTimeDN und PreLastVarDN, wobei

  • LastTimeDN die Zeit des letzten Extremum enthält,
  • LastVarDN den Wert des letzten Extremum enthält,
  • PreLastTimeDN die Zeit des vorletzten Extremum enthält und
  • PreLastVarDN den Wert des vorletzten Extremum enthält.

Danach ist der Code praktisch identisch zu dem oben bereits betrachteten, ausgenommen die verwendeten Variablen zum Zeichnen der Trendlinien und der Identifizierung von Überkreuzungen mit ihnen. Und schließlich sollten wir das Zurückgeben der Funktionswerte mit diesem letzten Schliff organisieren,

der im allgemeinen Fall wie folgt ist:

if (CrossGUP || CrossUP) return(1);
   else
   if (CrossGDN || CrossDN) return(-1); 
   else return(0);

Wo es eine Abwärts-Überkreuzung der Aufwärtstrend-Linie gibt, wird von der Funktion -1 zurückgegebenen, wo es eine Abwärts-Überkreuzung der Abwärtstrend-Linie gibt, wird von der Funktion 1 zurückgegeben, in allen anderen Fällen wird 0 zurückgegeben - so einfach ist es.

Es ist sicherlich besser alle Aufwärtstrend- und Abwärtstrend-Linien zu prüfen, entweder aufwärts oder abwärts, weisen Sie ihr eine "Gewichtung" zu, abhängig von der Kombination von Überkreuzungen und geben Sie den die erforderlichen Werte zur weiteren Signalverarbeitung an die aufrufende Funktion zurück.

Zum Abschluss der Funktionsbeschreibung werde ich ihren vollständigen Code bereitstellen, einschließlich einiger kleinerer gemachter Änderungen (mit der Prüfung ob es erlaubt ist eine Signalanzeige zu setzen, wenn eine Überkreuzung der Trendlinie und der A/D Chart Linie identifiziert ist, sowie der Verwendung der Standardfunktion iTime() anstelle von Time um die Allgemeingültigkeit der Funktion zu gewährleisten).

Der folgende bereitgestellte Code ist hoffentlich verständlich, nach der umfassenden Betrachtung oben:

//??????????????????????????????????????????????????????????????????????????????????????????
// Identification of a crossover of the trend line and the A/D chart line and setting of the signal indicators
//??????????????????????????????????????????????????????????????????????????????????????????
// TempIND[] - (declared on the global level) array for storing A/D indicator data
// nBars - number of bars for identification of extrema
// sy - working symbol: "" or NULL - current symbol of the chart
// tf - working time frame: 0 - current, 
// local - whether to calculate based on local trends: by default - false
// add - whether to react to the crossover of the trend in the direction of the trend: by default - false
// Arrow - whether to set signal indicators in the currency instrument chart 
//         when a crossover of the trend line and the A/D chart line is identified: by default - true
//==========================================================================================
int SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, 
                   bool local=false, bool add=false, bool Arrow=true)
{
   if (sy=="" || sy=="0") sy = Symbol();
   ArrayResize(TempIND,nBars);                     // Resize the array according to the size passed to the function
   for (int j=0; j<=nBars-1; j++)
      {
         TempIND[j]=iAD(sy,tf,j);                  // Write the indicator data in the loop into the array
      }
// ==========================================================================================               
// ------------------------------------ Function variables ---------------------------------+
// ==========================================================================================               

   double   PeakUP[], PeakDN[],                    // Declare peak/trough arrays
            yUP, yDN, yGUP, yGDN,                  // Coordinates of the crossing point between the trend line and indicator UP and DN on the 1st bar
            yUP2, yDN2, yGUP2, yGDN2,              // Coordinates of the crossing point between the trend line and indicator UP and DN on the 2nd bar
            CheckCross,
            PreLastVarDN,LastVarDN,                // Values of the last and last but one lower extrema
            PreLastVarUP,LastVarUP,                // Values of the last and last but one upper extrema
            PivotPeakDN,PivotPeakUP,
            LowestPeakDN,HighestPeakDN,            // Values of the lower minimum and maximum
            LowestPeakUP,HighestPeakUP,            // Values of the upper minimum and maximum

   datetime TimeDN[], TimeUP[],                    // Arrays for storing bars of extrema
            PreLastTimeDN, LastTimeDN,             // Time of the last and last but one lower extrema
            PreLastTimeUP, LastTimeUP,             // Time of the last and last but one upper extrema
            PivotBarDN, PivotTimeDN,               // Bar and time of the lower main extremum
            PivotBarUP, PivotTimeUP,               // Bar and time of the upper main extremum
            LowestBarDN, LowestTimeDN,             // Bar and time of the lower minimum
            HighestBarUP, HighestTimeUP;           // Bar and time of the upper maximum
   int      i, kup, kdn, pbar=2, 
            m, l, t, asize, Index,                 // "Internal" variables
            WinID=WindowFind("A/D");               // AD window number
   bool     CrossDN = false,                       // Flag indicating the downward crossover of the local trend
            CrossUP = false,                       // Flag indicating the upward crossover of the local trend
            CrossGDN = false,                      // Flag indicating the downward crossover of the global trend
            CrossGUP = false;                      // Flag indicating the upward crossover of the global trend
   
   double pt=MarketInfo(Symbol(),MODE_POINT);      // Point size in the quote currency

// ==========================================================================================               
// ------------------ Filling arrays with data on peaks and troughs ----------------------+
// ==========================================================================================               
         kdn=0;                                    // Initialize the trough array index
         for (i=2; i<=nBars-1; i++)                // Run through the array of values from the 2nd bar deep into history
            {
               if (TempIND[i]<TempIND[i-1] && 
                   TempIND[i+1]>=TempIND[i])       // Trough identified WAS THERE >=
                  {
                     ArrayResize(PeakDN, kdn+1);   // Resize the trough array according to the number of troughs identified
                     ArrayResize(TimeDN, kdn+1);   // Resize the trough time array according to the number of troughs
                     PeakDN[kdn]=TempIND[i];       // Write the trough value into the trough array...
                     TimeDN[kdn]=iTime(sy,tf,i);   // ...and the time array
                     kdn++;                        // Increase the trough array index
                  }
            } 
// -----------------------------------------------------------------------------------------------------------                       
         kup=0;                                    // Initialize the peak array index
         for (i=2; i<=nBars-1; i++)                // Run through the array of values from the 2nd bar deep into history
            {
               if (TempIND[i]>TempIND[i-1] &&      // WAS THERE >
                   TempIND[i+1]<=TempIND[i])       // Peak identified WAS THERE <=
                  {
                     ArrayResize(PeakUP, kup+1);   // Resize the peak array according to the number of peaks identified
                     ArrayResize(TimeUP, kup+1);   // Resize the peak time array according to the number of peaks
                     PeakUP[kup]=TempIND[i];       // Write its value into the peak array... 
                     TimeUP[kup]=iTime(sy,tf,i);   // ...and the time array
                     kup++;                        // Increase the peak array index
                  }
            }   
//====================================================================================================
// --------------------------- Identification of the lower minimum and main DN extremum -----------------+
//====================================================================================================
         PivotTimeDN = TimeDN[pbar];                        // Time of the main extremum
         PivotBarDN  = iBarShift(sy,tf,TimeDN[pbar]);       // Bar of the main extremum
         PivotPeakDN = PeakDN[pbar];                        // Value of the main extremum
         LowestPeakDN  = ArrayMin(PeakDN);                  // Get the lower minimum value
         Index = ArraySearchDouble(PeakDN, LowestPeakDN);   // Get the index of the lower minimum in the array
         LowestBarDN =iBarShift(sy,tf,TimeDN[Index]);       // Get the bar of the lower minimum
         LowestTimeDN = TimeDN[Index];                      // Get the time of the lower minimum

   if (LowestBarDN>PivotBarDN)                              // If the minimum is to the left of the first extremum ... 
                                                            // ... (being in 0 cell of the trough time array)
   for (m=Index-1; m>pbar; m--)                             // Loop from the extremum following the minimum to the first extremum
      {
// --------- Draw a virtual trend line and check it for crossovers with other extrema ----------  
                                  
         CheckCross=EquationDirect(iBarShift(sy,tf,TimeDN[m+1]),
                                   PeakDN[m+1],                      // First coordinate of the line
                                   PivotBarDN, PivotPeakDN,          // Second coordinate of the line
                                   iBarShift(sy,tf,TimeDN[m],false));// Crossing point associated with the next extremum
         if (TempIND[iBarShift(sy,tf,TimeDN[m])]<CheckCross)         // If the extremum lies below the line
            {
               if (PeakDN[m]<PeakDN[pbar])                           // if this extremum is lower than the main extremum
                  {
                     LowestBarDN =iBarShift(sy,tf,TimeDN[m]);        // Then this is the one we need to start from...
                     LowestPeakDN  = PeakDN[m];                      // New coordinates of the next extremum
                     LowestTimeDN = TimeDN[m];                             
                  }
            }                       
      }

      if (LowestBarDN>PivotBarDN && LowestPeakDN<PivotPeakDN)        // If the minimum is to the left of and below the main extremum
            {
// --------------- Draw an uptrend line for adjustment (DN - extrema) ---------------------                          
               if (WinID>0)
                  {
                     if (ObjectFind("Trend_GLine_DN")<0)
                     ObjectCreate("Trend_GLine_DN",OBJ_TREND,WinID,LowestTimeDN,LowestPeakDN,PivotTimeDN,PivotPeakDN);
                     ObjectSet("Trend_GLine_DN",OBJPROP_COLOR,Lime);
                     ObjectSet("Trend_GLine_DN",OBJPROP_TIME1,LowestTimeDN);
                     ObjectSet("Trend_GLine_DN",OBJPROP_PRICE1,LowestPeakDN);
                     ObjectSet("Trend_GLine_DN",OBJPROP_TIME2,PivotTimeDN);
                     ObjectSet("Trend_GLine_DN",OBJPROP_PRICE2,PivotPeakDN);
                  }
//---------------- Calculation of levels of the uptrend (DN - extrema) ----------------         
yGDN =EquationDirect(LowestBarDN, LowestPeakDN,PivotBarDN, PivotPeakDN, iBarShift(sy,tf,iTime(sy,tf,1),false));
yGDN2=EquationDirect(LowestBarDN, LowestPeakDN,PivotBarDN, PivotPeakDN, iBarShift(sy,tf,iTime(sy,tf,2),false));

//---------------- Draw levels of the uptrend (DN - extrema) ------------------            
         if (WinID>0)
            {
               if (ObjectFind("PointGDN"+iTime(sy,tf,1))<0)
               ObjectCreate("PointGDN"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yGDN);
               ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4);
               ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_COLOR,Lime);
               ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
               ObjectSet("PointGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,yGDN);
            }
// --------------- Check for a downward crossover of the uptrend -----------------   
         if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGDN,8) && 
             NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGDN2,8))
               {
                  CrossGDN = true;           // Set the flag indicating the downward crossover
                  CrossGUP = false;          // Remove the flag indicating the upward crossover
                  if (Arrow)                 // If the use of signal indicators is allowed
                     {
                        if (ObjectFind("ArrowGDN"+iTime(sy,tf,1))<0)
                        ObjectCreate("ArrowGDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+5*pt);
                        ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242);
                        ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed);
                        ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_WIDTH,1);
                        ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                        ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+5*pt);
                     }
               }
// --------------- Check for an upward crossover of the uptrend -----------------   
         
         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGDN,8) && 
                   NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGDN2,8))
                  {
                     CrossGUP = true;
                     CrossGDN = false;
                     if (Arrow)                 // If the use of signal indicators is allowed
                        {
                           if (ObjectFind("ArrowGUP"+iTime(sy,tf,1))<0)
                           ObjectCreate("ArrowGUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1));
                           ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241);
                           ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_COLOR,Lime);
                           ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_WIDTH,0);
                           ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                           ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1));
                        }
                  }
             }
        }                  
//====================================================================================================
// -------------------- Identification of the upper maximum and main UP extremum ----------------------+            
//====================================================================================================
         PivotTimeUP = TimeUP[pbar];                           // Time of the main extremum
         PivotBarUP  = iBarShift(sy,tf,TimeUP[pbar]);          // Bar of the main extremum
         PivotPeakUP = PeakUP[pbar];                           // Value of the main extremum
         
         HighestPeakUP = ArrayMax(PeakUP);                     // Get the value of the upper maximum
         Index = ArraySearchDouble(PeakUP, HighestPeakUP);     // Get the index of the upper maximum in the array
         HighestBarUP =iBarShift(sy,tf,TimeUP[Index]);         // Get the bar of the upper maximum
         HighestTimeUP = TimeUP[Index];                        // Get the time of the upper maximum

         if (HighestBarUP>PivotBarUP)                          // If the maximum is to the left of the first extremum ... 
                                                               // ... (being in 0 cell of the peak time array)
   for (m=Index-1; m>pbar; m--)                                // Loop from the extremum following the maximum to the first extremum
      {
// --------- Draw a virtual trend line and check it for crossovers with other extrema ----------  
    CheckCross=EquationDirect(iBarShift(sy,tf,TimeUP[m+1]), PeakUP[m+1], // First coordinate of the line
                              PivotBarUP, PivotPeakUP,                   // Second coordinate of the line
                              iBarShift(sy,tf,TimeUP[m],false));         // Crossing point associated with the next extremum
         if (TempIND[iBarShift(sy,tf,TimeUP[m])]>CheckCross)             // If the extremum lies above the line
            {
               if (PeakUP[m]>PeakUP[pbar])                                // if this extremum is higher than the main extremum
                  {
                     HighestBarUP =iBarShift(sy,tf,TimeUP[m]);            // Then this is the one we need to start from...
                     HighestPeakUP = PeakUP[m];                           // New coordinates of the next extremum
                     HighestTimeUP = TimeUP[m];                             
                  }
            }                       
      }
      if (HighestBarUP>PivotBarUP && HighestPeakUP>PivotPeakUP)           // If the maximum is to the left of the main extremum
         
            {
// ---------------- Draw a downtrend line (UP - extrema) ---------------------            
               if (WinID>0)
                  {
                     if (ObjectFind("Trend_Line_GUP")<0)
                     ObjectCreate("Trend_Line_GUP",OBJ_TREND,WinID,HighestTimeUP,HighestPeakUP,PivotTimeUP,PivotPeakUP);
                     ObjectSet("Trend_Line_GUP",OBJPROP_COLOR,OrangeRed);
                     ObjectSet("Trend_Line_GUP",OBJPROP_TIME1,HighestTimeUP);
                     ObjectSet("Trend_Line_GUP",OBJPROP_PRICE1,HighestPeakUP);
                     ObjectSet("Trend_Line_GUP",OBJPROP_TIME2,PivotTimeUP);
                     ObjectSet("Trend_Line_GUP",OBJPROP_PRICE2,PivotPeakUP);
                  }
//---------------- Calculation of levels of the downtrend (UP - extrema) ------------------            
         yGUP =EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,iTime(sy,tf,1),false));
         yGUP2=EquationDirect(HighestBarUP, HighestPeakUP, PivotBarUP, PivotPeakUP, iBarShift(sy,tf,iTime(sy,tf,2),false));

//---------------- Draw levels of the downtrend (UP - extrema) ------------------            
         if (WinID>0)
            {
               if (ObjectFind("PointGUP"+iTime(sy,tf,1))<0)
               ObjectCreate("PointGUP"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yGUP);
               ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4);
               ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed);
               ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
               ObjectSet("PointGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,yGUP);
            }
// --------------- Check for an upward crossover of the downtrend -----------------   
         if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yGUP,8) && 
             NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yGUP2,8))
               {
                  CrossGUP = true;                 // Set the flag indicating the upward crossover
                  CrossGDN = false;                // Remove the flag indicating the downward crossover
                  if (Arrow)                       // If the use of signal indicators is allowed
                     {
                        if (ObjectFind("ArrowGUP"+iTime(sy,tf,1))<0)
                        ObjectCreate("ArrowGUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1));
                        ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241);
                        ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_COLOR,Lime);
                        ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_WIDTH,1);
                        ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                        ObjectSet("ArrowGUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1));
                     }
               }
            
// --------------- Check for a downward crossover of the downtrend -----------------   
         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yGUP,8) && 
                   NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yGUP2,8))
                  {
                     CrossGDN = true;
                     CrossGUP = false;
                     if (Arrow)                    // If the use of signal indicators is allowed
                        {
                           if (ObjectFind("ArrowGDN"+iTime(sy,tf,1))<0)
                           ObjectCreate("ArrowGDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt);
                           ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242);
                           ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_COLOR,OrangeRed);
                           ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_WIDTH,0);
                           ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                           ObjectSet("ArrowGDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt);
                        }
                  }
            }
      }  
                                   
//???????????????????????????????????????????????????????????????????????????????????????????
// ==========================================================================================               
// ------------------ Identification of two (local) outermost lower DN extrema -----------------+      
// ==========================================================================================               
   if (local)
      {     
         asize=ArraySize(PeakDN);
         for (l=asize-1; l>=1; l--)
            {
               if (PeakDN[l]<PeakDN[l-1])
                  {
                     LastTimeDN     =TimeDN[l-1];
                     LastVarDN      =PeakDN[l-1];
                     PreLastTimeDN  =TimeDN[l];
                     PreLastVarDN   =PeakDN[l];
                  }
            }
// --------------- Draw a local uptrend line (DN - extrema) ---------------------            
         if (WinID>0)
            {
               if (ObjectFind("Trend_Line_DN")<0)
               ObjectCreate("Trend_Line_DN",OBJ_TREND,WinID,TimeDN[1],PeakDN[1],TimeDN[0],PeakDN[0]);
               ObjectSet("Trend_Line_DN",OBJPROP_COLOR,MediumSeaGreen);
               ObjectSet("Trend_Line_DN",OBJPROP_TIME1,PreLastTimeDN);
               ObjectSet("Trend_Line_DN",OBJPROP_PRICE1,PreLastVarDN);
               ObjectSet("Trend_Line_DN",OBJPROP_TIME2,LastTimeDN);
               ObjectSet("Trend_Line_DN",OBJPROP_PRICE2,LastVarDN);
            }
//---------------- Calculation of levels of the local uptrend (DN - extrema) ----------------            
 yDN =EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, 
                     iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,iTime(sy,tf,1),false));
 yDN2=EquationDirect(iBarShift(sy,tf,PreLastTimeDN,false), PreLastVarDN, 
                     iBarShift(sy,tf,LastTimeDN,false), LastVarDN, iBarShift(sy,tf,iTime(sy,tf,2),false));
               
//---------------- Draw levels of the local uptrend (DN - extrema) ------------------            
         if (WinID>0)
            {
               if (ObjectFind("PointDN"+iTime(sy,tf,1))<0)
               ObjectCreate("PointDN"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yDN);
               ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4);
               ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen);
               ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
               ObjectSet("PointDN"+iTime(sy,tf,1),OBJPROP_PRICE1,yDN);
            }
// --------------- Check for a downward crossover of the local uptrend -----------------   
         if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yDN,8) && 
             NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yDN2,8))
               {
                  CrossDN = true;
                  CrossUP = false;
                  if (Arrow)                 // If the use of signal indicators is allowed
                     {
                        if (ObjectFind("ArrowDN"+iTime(sy,tf,1))<0)
                        ObjectCreate("ArrowDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt);
                        ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242);
                        ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate);
                        ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_WIDTH,1);
                        ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                        ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt);
                     }
               }
// --------------- Check for an upward crossover of the local uptrend -----------------   
         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yDN,8) && 
                   NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yDN2,8))
                  {
                     CrossUP = true;
                     CrossDN = false;
                     if (Arrow)                 // If the use of signal indicators is allowed
                        {
                           if (ObjectFind("ArrowUP"+iTime(sy,tf,1))<0)
                           ObjectCreate("ArrowUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1));
                           ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241);
                           ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen);
                           ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_WIDTH,0);
                           ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                           ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1));
                        }
                  }
            }
      }
//====================================================================================================
// ------------------------- Identification of two (local) outermost upper UP extrema ------------------+       
//====================================================================================================
   if (local)
      {
         asize=ArraySize(PeakUP);
         for (l=asize-1; l>=1; l--)
            {
               if (PeakUP[l]>PeakUP[l-1])
                  {
                     LastTimeUP     =TimeUP[l-1];
                     LastVarUP      =PeakUP[l-1];
                     PreLastTimeUP  =TimeUP[l];
                     PreLastVarUP   =PeakUP[l];
                  }
            }
// ---------------- Draw a local downtrend line (UP - extrema) ---------------------            
         if (WinID>0)
            {
               if (ObjectFind("Trend_Line_UP")<0)
               ObjectCreate("Trend_Line_UP",OBJ_TREND,WinID,TimeUP[1],PeakUP[1],TimeUP[0],PeakUP[0]);
               ObjectSet("Trend_Line_UP",OBJPROP_COLOR,Chocolate);
               ObjectSet("Trend_Line_UP",OBJPROP_TIME1,PreLastTimeUP);
               ObjectSet("Trend_Line_UP",OBJPROP_PRICE1,PreLastVarUP);
               ObjectSet("Trend_Line_UP",OBJPROP_TIME2,LastTimeUP);
               ObjectSet("Trend_Line_UP",OBJPROP_PRICE2,LastVarUP);
            }
//---------------- Calculation of levels of the local downtrend (UP - extrema) ------------------            
 yUP =EquationDirect(iBarShift(sy,tf,PreLastTimeUP,false), PreLastVarUP, 
                     iBarShift(sy,tf,LastTimeUP,false), LastVarUP, iBarShift(sy,tf,iTime(sy,tf,1),false));
 yUP2=EquationDirect(iBarShift(sy,tf,PreLastTimeUP,false), PreLastVarUP, 
                     iBarShift(sy,tf,LastTimeUP,false), LastVarUP, iBarShift(sy,tf,iTime(sy,tf,2),false));

//---------------- Draw levels of the local downtrend (UP - extrema) ------------------            
         if (WinID>0)
            {
               if (ObjectFind("PointUP"+iTimeiTimesy,tf,1))<0)
               ObjectCreate("PointUP"+iTime(sy,tf,1),OBJ_ARROW,WinID,iTime(sy,tf,1),yUP);
               ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,4);
               ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate);
               ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
               ObjectSet("PointUP"+iTime(sy,tf,1),OBJPROP_PRICE1,yUP);
            }
// --------------- Check for an upward crossover of the local downtrend -----------------   
         if (NormalizeDouble(TempIND[1],8) > NormalizeDouble(yUP,8) && 
             NormalizeDouble(TempIND[2],8) <= NormalizeDouble(yUP2,8))
               {
                  CrossUP = true;
                  CrossDN = false;
                  if (Arrow)                 // If the use of signal indicators is allowed
                     {
                        if (ObjectFind("ArrowUP"+iTime(sy,tf,1))<0)
                        ObjectCreate("ArrowUP"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iLow(sy,tf,1));
                        ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_ARROWCODE,241);
                        ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_COLOR,MediumSeaGreen);
                        ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_WIDTH,1);
                        ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                        ObjectSet("ArrowUP"+iTime(sy,tf,1),OBJPROP_PRICE1,iLow(sy,tf,1));
                     }
               }           
// --------------- Check for a downward crossover of the local downtrend -----------------   
         if (add)
            {
               if (NormalizeDouble(TempIND[1],8) < NormalizeDouble(yUP,8) && 
                   NormalizeDouble(TempIND[2],8) >= NormalizeDouble(yUP2,8))
                  {
                     CrossDN = true;
                     CrossUP = false;
                     if (Arrow)               // If the use of signal indicators is allowed
                        {
                           if (ObjectFind("ArrowDN"+iTime(sy,tf,1))<0)
                           ObjectCreate("ArrowDN"+iTime(sy,tf,1),OBJ_ARROW, 0 ,iTime(sy,tf,1), iHigh(sy,tf,1)+15*pt);
                           ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_ARROWCODE,242);
                           ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_COLOR,Chocolate);
                           ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_WIDTH,0);
                           ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_TIME1,iTime(sy,tf,1));
                           ObjectSet("ArrowDN"+iTime(sy,tf,1),OBJPROP_PRICE1,iHigh(sy,tf,1)+15*pt);
                        }
                  }                         
            }
      }            
// -----------------------------------------------------------------------------------------------------------            
// The conditions here can be represented by CrossGUP and CrossGDN, as well as CrossUP and CrossDN.
// In the first case, we have a "global trend" on all specified bars,
// in the second case, we have local trends on the last two extrema.
// You can also combine signals of crossovers of "global" and "local" trends in the A/D chart
/*   
   if (CrossGUP && CrossUP)   return(11);    // An upward crossover of both downtrends
   else
   if (CrossGDN && CrossDN)   return(-11);   // A downward crossover of both uptrends
   else
   if (CrossGUP)              return(10);    // An upward crossover of the "global" downtrend
   else
   if (CrossGDN)              return(-10);   // A downward crossover of the "global" uptrend
   else
   if (CrossUP)               return(1);     // An upward crossover of the "local" downtrend
   else
   if (CrossDN)               return(-1);    // A downward crossover of the "local" uptrend
*/   
   if (CrossGUP || CrossUP) return(1);
   else
   if (CrossGDN || CrossDN) return(-1); 
   else return(0);
                      
}  

Der obige Code ist fast der gleiche wie der, den wir bereits analysiert haben. Aus diesem Grund werde ich nur die Eingabeparameter der Funktion und die Methode ihres Aufrufs kommentieren.

int   SignalCrossIND(double &TempIND[], int nBars, string sy, int tf, bool local=false, bool add=false, bool Arrow=true)

Im Gegensatz zu dem vorher betrachteten Code, haben wir hier eine weitere Variable des bool Typs - Pfeil hinzugefügt, welche die Signalanzeige in dem Hauptfenster des Terminals anzeigt. Der Standardwert ist ist festgelegt auf true, das heißt, wenn die Pfeile im Chart angezeigt werden, kann dieser Parameter bei dem Aufruf der Funktion ausgelassen werden.

Im Allgemeinen kann der Funktionsaufruf wie folgt aussehen. Fügen Sie die folgende Zeile in die globale Variable eines EA ein:

double      TempIND[];           // Array for storing indicator data

Dann geben Sie die gewünschte Anzahl an Balken der Historie zur Ermittlung der Extrema in dem A/D-Indikator-Chart an, für den Empfang der Handelssignale.

int nBrs=30;

oder, zum Beispiel

int nBrs=200;

Hier hängt alles davon ab, welche TF Trendlinien gezeichnet werden und wie viele Balken der Historie wir verarbeiten werden. Je größer der Wert, desto "stabiler" werden die Trendlinien in dem A/D-Chart sein, doch das Überkreuzung-Signal würde mehr verzögert werden./p>

Und schließlich der Funktionsaufruf als solcher:

int sig=0;
   sig=SignalCrossIND(TempIND, nBrs, NULL, 5, false, false, true);
   
   if (sig==1)
      {Code for opening a Buy position}
   if (sig==-1)
      {Code for opening a Sell position}

Die hier übergebenen Parameterwerte legen nahe, dass die zu erhaltenen Daten aus dem Chart des aktuellen Währungsinstruments (NULL) sind, der zu verwendende Zeitrahmen ist M5 (5), lokale Trendlinien sind nicht gezeichnet und es wird nicht nach Überkreuzungen dieser Linien gesucht false), ferner wird nicht nach einer Überkreuzung in Trendrichtung gesucht (false), während die Signalanzeigen (Auf/Ab Pfeile) in dem Haupt-Chartfenster angezeigt werden (true).

Da dies alles Standardwerte sind, wird die folgende Funktion absolut identisch betrachtet werden:

sig=SignalCrossIND(TempIND, nBrs, NULL, 5);


Fazit

Um das obige abzuschließen möchte ich sagen, dass diese Funktion keinesfalls eine unabhängige Handelsstrategie ist. Es ist lediglich eine Funktion zum Ermitteln von Überkreuzungen von in dem A/D Indikator-Chart gezeichneten Trendlinien und der Indikatorlinie. Sie ist gut in einem EA zusammen mit anderen Funktionen zu verwenden.

Um seine Fähigkeiten zu sehen, führte ich einen Test über den Zeitraum vom 01.01.2009 bis zum 01.01.2010 aus. Der Test wurde nur mit den von dieser Funktion zurückgegeben Werten ausgeführt. Ich habe das Signalmodul des EAs deaktiviert und einzig mit dieser Funktion ersetzt. Die Anzahl der Balken für die Identifizierung der Extrema = 250. Der Zeitrahmen = 5, das gewählte Symbol war EURUSD, Die anfängliche Einzahlung war 10000, das festgelegte Lot war 0.1.

Ich habe außerdem die Verwendung von SL und TP deaktiviert. Ich habe lediglich den Trailing Stop belassen, der die Positionen teilweise in drei Schritten schließt, nach dem Erreichen der des eingestellten Gewinn-Niveaus (welches, natürlich, für alle drei Stufen unterschiedlich ist). Positionen werden geöffnet, wenn ein Signal von der Funktion empfangen wird.

Die gegenteilige Position wurde in diesem Fall nicht geschlossen. Nach dem Empfang des Signals in Richtung der bereits bestehenden Position, musste ich die Zeit bestimmen, die nach dem Öffnen der vorherigen Position vergangen ist, und wenn es mehr als 7 Minuten waren, wurde eine neue Position geöffnet. Kurz gesagt, ich habe die Einzahlung bis zum Maximum verwendet... :) Eine andere Sache, die ich fast vergessen habe zu erwähnen ist, dass ich zum Schließen aller Positionen einen festgelegten Prozentsatz des Kapitals verwendet habe. In diesem Test habe ich 5% verwendet.

Das Daten über ein Jahr darstellende Ergebnis-Diagramm sieht aus wie folgt:

Es ist klar, dass es nicht auf einem realen Handelskonto angewendet werden kann, aber ich glaube, es liegt ein sinnvoller Kern in ihr (umso mehr, da es nur eine Signalfunktion ist).

Und abschließend möchte ich meinen tiefen, aufrichtigen Dank ausdrücken an Viktor (Vinin) und Aleksey (Mathemat) für ihre selbstlose Hilfe und die wirklich freundliche Unterstützung, sowie an all diejenigen, die auf die eine oder andere Weise zur Lösung verschiedener Programmierfragen beigetragen haben.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1357

MetaTrader 4 unter Linux MetaTrader 4 unter Linux
In diesem Artikel demonstrieren wir eine einfache Möglichkeit, MetaTrader 4 auf gängigen Linux-Versionen zu installieren – Ubuntu und Debian. Diese Systeme werden häufig auf Serverhardware sowie auf den Personalcomputern von Händlern verwendet.
MetaTrader 4 auf Mac OS MetaTrader 4 auf Mac OS
Apple Produkte sind sehr beliebt geworden. MetaQuotes Software Corp. folgt aufmerksam dem Fortschritt in der IT-Industrie und hat bereits spezielle mobile Anwendungen für iOS-basierte Geräte veröffentlicht - MetaTrader 4 für iPhone und MetaTrader 5 für iPhone. Es gibt viele Themen in dem MQL4.Community Forum, in denen die Menschen nach der Ausführung von MetaTrader 4 unter dem Mac OS Betriebssystem suchen. In diesem Artikel werden Sie herausfinden, wie MetaTrader 4 über Apples beliebtes Betriebssystem funktioniert.
Die Random Sandbox Die Random Sandbox
Der Artikel enthält eine interaktive "Sandbox" als Excel-Datei, die randomisierte Expert Advoisor Backtest-Daten simuliert. Leser können diese verwenden um die Metriken der EA-Leistungen, die standardmäßig von MetaTrader angeboten werden, zu erkunden und tiefer zu verstehen. Der Test dieses Artikels wurde entworfen um Nutzer durch diese Erfahrung zu führen.
Traders Kit: Drag Trade Library Traders Kit: Drag Trade Library
Der Artikel beschreibt die Drag Trade Library, die Funktionalität für visuelles Trading bereitstellt. Die Bibliothek kann in nahezu jeden Expert Advisor integriert werden. Ihr Expert Advisor kann fast mühelos von einem Automaten in ein Handels- und Informationssystem umgewandelt werden, durch einfaches Hinzufügen von ein paar Zeilen Code.