Darstellung der Unterstützung / Widerstand Levels

Slobodov Gleb | 1 März, 2016

Einführung

Der Artikel widmet sich der Suche und Darstellung der Unterstützung / Widerstand Levels in MetaTrader 4. Basierend auf einem einfachen Algorithmus wird der komfortable und universelle Indikator FindLevels konstruiert, der horizontalen Linien von Unterstützungslevels im Chart anzeigt. Sie können es unten sehen:

Der Artikel befasst sich auch mit dem Thema, wie man einen einfachen Indikator erstellen soll, der die Ergebnisse von verschiedenen Zeitrahmen in einem Arbeitsbereich anzeigen kann. Wie es aussieht, können Sie unten im Bild sehen.

Der erste Indikator liefert Unterstützung Levels (dicke beige Linien), die auf Basis der Notierungen von dem 30 Minütigen-Zeitrahmen geliefert werden. Der zweite Indikator, der im gleichen Fenster gestartet wird, zeigt die Levels auf Basis von dem 15-Minütigen-Zeitrahmen (dünne gestrichelte violette Linien), die übrigens über den Levels vom 30-Minütigen-Indikator geliefert werden. Siehe den Abschnitt "Indikatoren Wechselwirkung" für weitere Details.


Der vorliegende Artikel ist eine Fortsetzung des Artikels über die Erstellung eines Skripts, der die Unterstützung Levels berechnet, aber der Unterschied ist zwischen ihnen, dass dieser Artikel für fortgeschrittene Benutzer in der Programmierung und der Arbeit im MetaTrader 4-Plattform geschrieben ist. Deshalb empfehle ich für die Anfänger und alle, die den Artikel zu kompliziert finden, erstmal meinen vorherigen Artikel zu lesen «Ein Verfahren zur Erstellung der Unterstützung / Widerstand Levels».


Theorie

Lassen Sie uns den Algorithmus der Unterstützung / Widerstand Levels beschreiben, damit wir den weiter im Indikator FindLevels realisieren. Ein Unterstützung / Widerstand Level ist ein Preis-Wert, der aufgrund einiger Kräfte nicht gekreuzt werden kann. Es kann die Kräfte sein, die durch einen psychologischen Punkt verursacht werden können oder es ist ein Einfluss von einigen großen Spielern oder es ist eine große Menge von Stoploss Orders, die in diesem Bereich des Levels platziert wurden. Es ist offensichtlich, dass Notierungen diese Linie seltener kreuzen werden als die anderen Linien, die keine Unterstützungslevels sind. Sie können die Beweise für diese Tatsache in vielen Handels-Büchern finden.

Um die Vorteile dieser Tatsache zu benutzen, müssen wir die Anzahl der Bars für jeden Preis berechnen, die den Preis kreuzen. Das ist ein Chart der Ergebnisse von solchen Berechnungen aus dem vorhergehenden Artikel:

Die horizontale Achse in diesem Bild ist ein Preis; in der vertikalen Achse wurde die Anzahl der Bars gezeichnet, die den Preis kreuzen. Wie Sie im Bild sehen können, hat das Bild eine Menge der lokalen Minima. Ein lokales Minimum ist ein Punkt eines Nicht-Nullwertiger Intervalls, in dem dieser Punkt ein Minimum ist. Jetzt müssen wir eine Auswahl über lokalen Minima nach einem bestimmten Kennzeichnen durchführen.

Erstens wird eine konstante MaxR - der Radius des Umkreises eingegeben. Wenn ein lokales Minimum kein Minimum im Umkreis mit dem Radius MaxR ist, dann passt uns dieses lokale Minimum nicht. Zweitens geben wir den Parameter MaxCrossesLevel ein. Wenn das Maximum der Funktion sich im Umkreis mit dem Radius MaxR von dem Minimum weniger unterscheidet als im MaxCrossesLevel, dann werden wir dieses lokale Minimum auch nicht anzeigen, denn er ist nicht genug auffällig.

Das ist der ganze Algorithmus der Suche. Im Anschluss zu diesem einfachen Algorithmus werden wir den Indikator schreiben.


Hilfsfunktionen

Wie oben beschrieben wurde, ist der Indikator FindLevels für die Arbeit mit Notierungen von einem Zeitrahmen vorgesehen, die vom Benutzer eingegeben werden müssen (Die Variable Timeperiod). Um den Code zu vereinfachen, werden wir zwei einfache Funktionen eingeben, die auch ohne zusätzliche Erklärungen klar sind:

double prLow(int i)
  {
    return (iLow(NULL,TimePeriod,i));
  }
 
double prHigh(int i)
  {
    return (iHigh(NULL,TimePeriod,i));
  }

Die dritte und die vierte Funktionen werden gebraucht, um die Unterstützung-Levels, ihre Breite, Farbe und die Reihe der Darstellung in der Abhängigkeit von Zeitrahmen bequem darzustellen:

int Period2Int(int TmPeriod)
  {
    switch(TmPeriod)
      {
        case PERIOD_M1  : return(0);
        case PERIOD_M5  : return(1);
        case PERIOD_M15 : return(2);
        case PERIOD_M30 : return(3);
        case PERIOD_H1  : return(4);
        case PERIOD_H4  : return(5);
        case PERIOD_D1  : return(6);
        case PERIOD_W1  : return(7);
        case PERIOD_MN1 : return(8);
      }      
    return (0);
  }
 
string Period2AlpthabetString(int TmPeriod)
  {
    return(Alphabet[Period2Int(TmPeriod)]); 
  }
     

Der Sinn der Funktion Period2AlphabetString() wurde im Abschnitt «Indikatoren Wechselwirkung» erklärt, und die Verwendung der Funktion Period2Int() wird bereits im nächsten Abschnitt erklärt.


Das Schreiben des Indikators

Beginnen wir mit den externen Variablen, die vom Benutzer eingegeben werden:

extern int MaxLimit = 1000;
extern int MaxCrossesLevel = 10;
extern double MaxR = 0.001;
extern int TimePeriod = 0;
extern color LineColor = White;
extern int LineWidth = 0;
extern int LineStyle = 0; 

Wenn die Werte der LineColor, LineWidth oder LineStyle vom Benutzer standardmäßig eingestellt wurden, dann werden wir die Werte im Init-Verfahren um die Werte ändern, die vom Zeitrahmen abhängig werden. Das wird zwecks der Aussicht der Linien von verschiedenen Zeitrahmen gemacht, damit sie nicht identisch werden und man könnte sie unterscheiden.

int init()
  {
    if(TimePeriod == 0)
        TimePeriod = Period();
 
    if(TimePeriod != 0 && LineWidth == 0)
        if(Period2Int(TimePeriod) - Period2Int(Period()) >= 0)
            LineWidth = Widths[Period2Int(TimePeriod) - Period2Int(Period())];
        else
          {
            LineWidth = 0;
            if(LineStyle == 0)
                LineStyle = STYLE_DASH;
          }
 
    if(TimePeriod != 0 && LineColor == White)
        LineColor = Colors[Period2Int(TimePeriod)];
 
    return(0);
  }

In der ersten Zeile stellen wir den Wert Timeperiod ein, wenn sie standardmäßig eingestellt ist. Dann bestimmen wir die Linienbreite. Je mehr die Timeperiode in Bezug auf den Zeitrahmen des Charts (Mapping-Fenster) ist, desto größer sind die Linien. Wenn die Timeperiode weniger als die Periode des Charts ist, dann ist die Linienbreite 0 und die Linie selber ist gestrichelt. Jede zeitliche Periode hat ihre eigene Farbe.

Die Arrays Colors[] und Width[] werden in der folgenden Weise definiert:

color  Colors[] = {Red,Maroon, Sienna, OrangeRed, Purple,I ndigo,
                   DarkViolet, MediumBlue, DarkSlateGray};
int    Widths[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

Lassen Sie uns die verbleibenden Variablen definieren:

int CrossBarsNum[];
bool CrossBarsMin[];
double d1Num = 0.0, d2Num = 0.0;
datetime TMaxI = 0;
#define MaxLines 1000
string LineName[MaxLines];
int LineIndex = 0;

Lassen Sie uns auf die Funktion start() gehen:

int counted_bars = IndicatorCounted();
int limit = MathMin(Bars - counted_bars, MaxLimit);
 
double d1 = prLow(iLowest(NULL, TimePeriod, MODE_LOW, limit, 0));
double d2 = prHigh(iHighest(NULL, TimePeriod, MODE_HIGH, limit, 0));

Wir berechnen die Variable limit, welche die Anzahl der Bars verwendet, die nicht nach dem letzten Aufruf des Indikators geändert worden ist. d1 und d2 – sind der minimale und der maximale Preise im Intervall von 0 bis limit.

if(d1Num != d1 || d2Num != d2)
  {
    ArrayResize(CrossBarsNum, (d2 - d1)*10000);
    ArrayResize(CrossBarsMin, (d2 - d1)*10000);
 
    if(d1Num != d1 && d1Num != 0.0)
      {
        ArrayCopy(CrossBarsNum, CrossBarsNum, 0, (d1Num - d1)*10000);
        ArrayCopy(CrossBarsMin, CrossBarsMin, 0, (d1Num - d1)*10000);
      }
 
    d1Num = d1;
    d2Num = d2;
  }

Während der Arbeit des Indikators, können die Preisunterschiede mit dem CrossBarsNum [] und CrossBarsMin [] Arrays sich ändern. Jedes Mal, wenn es passiert, müssen wir die Anzahl der Zellen im Array erhöhen und sie bei Bedarf auf die rechte Seite bewegen. Es geschieht in der Regel, wenn die neuen Variablen d1, d2 nicht mit den Variablen d1Num, d2Num gleich sind, die bei dem vorherigen Aufruf der Funktion start() bekommen wurden.

for(double d = d1; d <= d2; d += 0.0001)
  {
    int di = (d - d1)*10000;
    for(int i = 1; i < limit; i++)
        if(d > prLow(i) && d < prHigh(i))
            CrossBarsNum[di]++;
    if(Time[limit] != TMaxI&&TMaxI != 0)
        if(d > prLow(iBarShift(NULL, 0, TMaxI)) && 
           d < prHigh(iBarShift(NULL, 0, TMaxI)))
            CrossBarsNum[di]--;
  }
TMaxI = Time[limit] - 1;

Nachdem wir festgestellt haben, dass unsere Arrays den notwendigen Dimensionen entsprechen, beginnen wir die neuen Bars für jeden Preis zu berechnen und erhöhen den Wert des CrossBarsNum [], wenn die Bar des Preis-Levels kreuzt. Von daher, dass neue Bars ständig entstehen, werden die alten Bars von dem Intervall [0 : limit] entfernt. Aus diesem Grund müssen wir solche Bars überprüfen und den Wert des CrossBarsNum [] verringern, falls es eine Kreuzung wird. Dann wird die Variable TmaxI die Zeit des letzten berechneten Bars verliehen.

double l = MaxR*10000;
for(d = d1 + MaxR; d <= d2 - MaxR; d += 0.0001)
  {
    di = (d - d1)*10000;
    if(!CrossBarsMin[di] && CrossBarsNum[ArrayMaximum(CrossBarsNum, 2*l, di - l)] - 
       CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)] > MaxCrossesLevel &&
       CrossBarsNum[di] == CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)] &&
       CrossBarsNum[di-1] != CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)])
      {
        CrossBarsMin[di] = true;
        LineName[LineIndex] = Period2AlpthabetString(TimePeriod) + TimePeriod + "_" + d;
        ObjectCreate(LineName[LineIndex], OBJ_HLINE, 0, 0, d);
        ObjectSet(LineName[LineIndex], OBJPROP_COLOR, LineColor);
        ObjectSet(LineName[LineIndex], OBJPROP_WIDTH, LineWidth);
        ObjectSet(LineName[LineIndex], OBJPROP_STYLE, LineStyle);
        LineIndex++;
      }
    if(CrossBarsMin[di] && CrossBarsNum[di] != 
       CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)])
      {
        CrossBarsMin[di] = false;
        ObjectDelete(Period2AlpthabetString(TimePeriod) + TimePeriod + "_" + d);
      }          
  }

Am Ende des Verfahrens start() überwachen wir das Array CrossBarsMin [] neu, um die neuen lokalen Minima zu finden, und die alten zu löschen, die nicht mehr die lokalen Minima sind. Da es mehrere als nur ein lokales Minimum sein können (es gibt mehrere passende Werte im Array CrossBarsMin [] und sie sind alle die lokalen Minima), müssen wir nur einen von ihnen liefern. Wir werden das lokale Minimum mit dem niedrigsten Preis liefern:

CrossBarsNum[di] == CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)] &&
CrossBarsNum[di-1]!= CrossBarsNum[ArrayMinimum(CrossBarsNum, 2*l, di - l)]

Es ist nicht kompliziert, ein neues grafisches Objekt, eine horizontale Linie zu erstellen. Wir stellen die Eigenschaften für diese Linie ein: die Breite, den Style, die Farbe, die zuvor in dem Init-Verfahren entwickelt wurden. Es ist nicht kompliziert, die Levels zu löschen, die nicht mehr Unterstützungslevels sind. Es ist noch ein Punkt nicht klar, warum wir in dem Namen des Objekts die Funktion Period2AlpthabetString (Timeperiod) verwenden. Der nächste Abschnitt beschreibt das Problem, das ich schon mehrmals erwähnt habe.


Indikatoren Wechselwirkung

Wie es schon am Anfang des Artikels erwähnt wurde, wird der Indikator FindLevels zur Darstellung der Unterstützungslevels von mehreren Zeitrahmen im Chart vorgesehen. Dafür brauchen wir:

  1. Es sollte möglich sein, den Indikator mehrmals starten zu können und der muss die Eingangsdaten im Zusammenhang mit einer Zeitperiode haben;
  2. Die einzelnen Linien sollten unterschiedlich sein, und wir sollten einfach bestimmen können, zu welcher Zeitdauer jeder Unterstützungslevel gehört;
  3. Alle Linien sollten sowohl aus langen und kurzen Zeitraumen sichtbar sein.

Der erste Punkt wird ohne Probleme ausgeführt. Wir haben keine globalen Variablen. Es gibt verschiedenen Namen der graphischen Objekte für jeden Zeitraum, denn wir haben in dem Namen des Objekts einen Zeitraum, (Zum Beispiel: «f30_1.25600000», 30 – Zeitperiode), und dadurch wird es keine Konflikte geben, wenn wir mehrere Indikatoren aufrufen.

Der zweite Punkt ist auch realisierbar, denn jede Linie hat ihre Farbe je nach dem Zeitraum (LineColor=Colors[Period2Int(TimePeriod)]).

Jetzt haben wir noch nur den dritten Punkt. Es ist ganz logisch, wenn eine Linie geraten wurde als die Unterstützungslinie für ein 5-Minütigen-Chart, dann wird die auch eine Unterstützungslinie für ein 30-Minütigen-Chart sein. Wenn diese Linien den gleichen Preis und die gleiche Breite haben, dann wird eine Linie für uns einfach nicht sichtbar! Deshalb sollten die Linien aus unterschiedlichen Zeitrahmen auch unterschiedliche Breite haben. Wir werden die Unterstützungslevels mit den längeren Zeitrahmen breiter als die Linien mit den kürzeren einstellen. So wird es durchaus sinnvoll, weil die Linien, die aus längeren Zeitrahmen ausgebaut wurden, haben eine mehrere Bedeutungen.

Wir müssen nun die richtige Reihe der Linien-Darstellung durchsetzen. Die dünneren Linien sollten am Ende angezeigt werden und dadurch müssen sie die dickeren Linien überlagern, damit man sie deutlich erkennen könnte. Im MetaTrader 4-Programm werden die Objekte in alphabetischer Reihenfolge angezeigt. So ist es notwendig, damit die Linien-Namen mit der längeren Perioden alphabetisch vor den Linien mit kürzeren Perioden kommen würden. Deshalb haben wir die Funktion erstellt, die ein lateinisches Buchstaben in der Abhängigkeit von dem Zeitrahmen setzt:

string Period2AlpthabetString(int TmPeriod)
  {
    return (Alphabet[Period2Int(TmPeriod)]);
  }

Alphabet - ist ein Array der lateinischen Buchstaben in umgekehrter Reihenfolge. Der vollständige Name jedes Unterstützungslevels sieht so aus: Period2AlpthabetString(TimePeriod)+TimePeriod+"_"+d.

Zum besseren Verständnis möchte ich ein Screenshot aus dem Anfang des Artikels wiederholen:


Schlussfolgerungen

Die Überprüfung des Indikators, den ich bereits überprüft habe, hat gezeigt, dass der gut funktioniert. Der ist leicht zu verwenden, weil der die Daten von den verschiedenen Zeitrahmen anzeigen kann. Der Test hat auch gezeigt, dass es bequemer ist, wenn der Indikator 3-10 Unterstützungslevels für jede Timeperiode zeigt. Um dies zu erreichen, müssen wir die entsprechenden Eingangsdaten MaxR und MaxCrossesLevel wählen. Während meines Tests variierte MaxR von 0,0003 für kürzere Zeiträume bis 0,002 für längere. MaxCrossesLevel - von 3 bis 20. Vielleicht wäre es hilfreicher gewesen, den Indikator so zu konfigurieren, damit er die bestimmte Anzahl der wichtigsten Unterstützungslevels anzeigen würde, aber das hätte den Code komplizierter gemacht. Außerdem denke ich, dass diejenige, die mein Indikator nicht gut gefunden haben, können eins selber erstellen.