Die MQL4-Programmiersprache für Neueinsteiger: Individuelle Indikatoren (Teil 1)

Antoniuk Oleg | 9 Februar, 2016

Einführung

Dies ist der vierte Artikel aus der Reihe " Die MQL4-Programmiersprache für Neueinsteiger ". Heute werden wir lernen, individuelle Indikatoren zu erstellen. Wir werden uns mit der Klassifizierung von Indikatorfunktionen vertraut machen und erfahren, wie diese Funktionen den Indikator beeinflussen. Wir werden neue Funktionen und Optimierungsmöglichkeiten kennenlernen und schließlich unsere eigenen Indikatoren programmieren. Darüber hinaus werden wir Ihnen zum Schluss des Artikels einige Tipps zum Programmierstil an die Hand geben. Falls dies der erste Artikel aus der Reihe „für Neueinsteiger“ sein sollte, welchen Sie lesen, dann wäre es möglicherweise besser für Sie, zunächst die vorhergehenden Artikel aus dieser Reihe zu lesen. Darüber hinaus sollten Sie zunächst sicherstellen, dass Sie die bereits behandelten Themen gut verstanden haben, weil dieser Artikel nämlich die Grundlagen der Programmierung nicht erneut behandelt.


Die verschiedenen Typen von Indikatoren

Jetzt werde ich Ihnen zeigen, welche verschiedenen Typen von Indikatoren es gibt. Natürlich haben Sie bereits eine Menge davon kennengelernt, aber ich möchte Ihre Aufmerksamkeit auf die speziellen Funktionen und Parameter der einzelnen Indikatoren lenken, und daher werden wir uns eine kurze Klassifizierung der Funktionen und Parameter ansehen. Dies wird Ihnen dann dabei behilflich sein, Ihre eigenen individuellen Indikatoren zu erstellen. Jetzt wollen wir also mal einen Blick auf einen ersten, ganz einfachen Indikator werfen :

Dies ist der Gleitende Durchschnitt (Englisch: „Moving Average“, abgekürzt MA) , ein weit verbreiteter und vielbenutzter technischer Indikator. Bitte beachten Sie hierbei die folgenden wichtigen Gegebenheiten :

  • Der Indikator wird im Chart-Fenster gezeichnet
  • Der Indikator zeigt lediglich einen Wert
  • Die Bandbreite der Indikatorwerte ist unbegrenzt und sie hängt von den aktuellen Kursen ab
  • Die Linie wird in einer bestimmten Farbe , mit einer bestimmten Breite und in einem bestimmten Stil (eine durchgehende Linie) gezeichnet

Lassen Sie uns jetzt einen Blick auf einen weiteren Indikator werfen:

Es handelt sich hierbei um den Williamschen Prozentbereich, %R. Bitte beachten Sie die folgenden wichtigen Gegebenheiten:

  • Der Indikator wird in einem separaten Unterfenster gezeichnet
  • Wie im vorherigen Fall zeigt dieser Indikator lediglich einen Wert an
  • Die Bandbreite der Indikatorwerte ist stark begrenzt
  • Die gezeichnete Linie verfügt über einen anderen Stil , eine andere Farbe und eine andere Breite

Daher gelten die folgenden Indikatoreigenschaften :

  • Der Indikator wird wie folgt gezeichnet: In einem Chart-Fenster oder in einem separaten Unterfenster. Wir möchten nun den Grund dafür herausfinden, weshalb der Gleitende Durchschnitt auf dem Chart gezeichnet wird, und der Williamsche Prozentbereich, %R dagegen in einem separaten Fenster. Der Unterschied liegt in der Bandbreite der angezeigten Werte. Beachten Sie hierbei, dass der zweite Indikator Werte im Bereich von 0 bis -100 anzeigt. Stellen Sie sich nun einmal vor, dass wir diese Werte in einem Chart-Fenster anzeigen würden. Was würde dann wohl geschehen? Sie würden diese Linie überhaupt nicht zu Gesicht bekommen, weil der Kurs über eine viel engere Bandbreite verfügt. In unserem Fall bewegt sich diese von 0,6805 bis 0,7495. Aber das ist noch längst nicht alles. Tatsächlich sind die Kurse nämlich positive Zahlen, und unser Wert ist dagegen negativ. Die Indikatoren werden in einem separaten Unterfenster gezeichnet, falls sich ihre Werte außerhalb der Kursspanne des aktiven Charts bewegen. Und immer dann, wenn die Bandbreite fast identisch ist (wenn es sich beispielsweise um verschiedene Arten von gleitenden Durchschnitten handeln sollte), dann wird der Indikator in einem Chart-Fenster gezeichnet. In Zukunft sollten Sie diesen Indikatorparameter gemäß der folgenden einfachen Grundsätze festlegen. Hier sehen Sie eine Illustration:

  • Ein Indikator, der in einem separaten Unterfenster gezeichnet wird, kann potentiell auf einen stark begrenzten Wertebereich limitiert sein. Dies bedeutet, dass das Terminal eine fixe Skala festlegt, um die Indikatorwerte anzuzeigen; und selbst dann, wenn die Werte diese Bandbreite übersteigen sollten, werden Sie diese nicht zu Gesicht bekommen. Falls Sie diesen Parameter deaktivieren, dann wird das Terminal automatisch die Skala so verändern, dass sie sämtliche Werte des Indikators beinhaltet. Sehen Sie sich hierzu die folgende Illustration an:

  • Ein Indikator ist potenziell in der Lage, seine Werte in verschiedenen Farben, Stilen und Breiten anzuzeigen. Sie haben dies bestimmt schon oft bemerkt, wenn Sie die Einstellungen für die Anzeige von Indikatoren im Terminal festgelegt haben. Hierbei gibt es eine Einschränkung: Falls Sie eine Linie mit einer Breite von mehr als 1 verwenden, dann können Sie lediglich einen Stil benutzten, nämlich die durchgehende Linie.

Hier ist ein weiterer Indikator :



Wie Sie sehen können, wird der Indikator Volumen in der Form eines Histogramms gezeichnet. Es gibt also mehrere verschiedene Möglichkeiten zur Anzeige der Indikatorwerte . Hier ist ein Beispiel eines weiteren Typs:



Der Indikator Fraktale wird in Form spezieller Symbole gezeichnet. Werfen Sie jetzt einmal einen Blick auf den folgenden Indikator:



Dies ist der sogenannte Alligator. Beachten Sie, dass dieser Indikator gleichzeitig drei verschiedene Werte darstellt (Bilanzlinien). Wie funktioniert das? Tatsächlich verwendet jeder Indikator (es gibt natürlich einige Ausnahmen, aber die werden wir später behandeln) Datenpuffer zur Anzeige seiner Werte.

Der Datenpuffer stellt praktisch eine simple Matrix dar. Seine Besonderheit liegt in der Tatsache, dass diese Matrix teilweise durch das Terminal verwaltet wird. Das Terminal verändert die Matrix in einer solchen Art und Weise, dass mit der Registrierung jedes neuen Balkens eine Verschiebung stattfindet. Dies dient dazu, dass jedes einzelne Element der Matrix einem bestimmten Balken zugeordnet werden kann. Die Maximalzahl der angezeigten Datenpuffer in einem Indikator beträgt 8. Dies mag Ihnen zunächst seltsam erscheinen, aber schon bald werden Sie verstehen, dass es überhaupt nicht anders funktionieren kann. Rufen Sie sich hierzu nur noch einmal in Erinnerung, dass es einen separaten Datenpuffer für jede Linie im Alligator-Indikator gibt. Jeder einzelne Datenpuffer verfügt über seine eigenen Parameter, gemäß derer das Terminal die dazugehörigen Linien zeichnet. In unserem Fall gibt es drei Datenpuffer, die man auf die folgende Art und Weise beschreiben könnte:

  1. Der erste Puffer wird in Form einer durchgehenden grünen Linie mit einer Breite von 3 gezeichnet.
  2. Der zweite Puffer wird in Form einer gestrichelten Linie in roter Farbe und einer Breite von 1 gezeichnet.
  3. Der dritte Puffer wird in Form einer durchgehenden blauen Linie mit einer Breite von 2 gezeichnet.

Es ist für einen Indikator nicht notwendig, einen Datenpuffer zu zeichnen. Dieser kann ebenfalls für Zwischenberechnungen benutzt werden. Dies ist auch der Grund dafür, weshalb die Anzahl der verwendeten Datenpuffer größer sein kann als die gezeichneten Linien, welche Sie zu Gesicht bekommen. Aber die wichtigste Eigenschaft von Datenpuffer liegt darin, dass es möglich sein muss, jedes einzelne Pufferelement einem bestimmten Balken auf dem Chart zuzuordnen. Behalten Sie dieses Faktum stets im Hinterkopf. Schon bald werden Sie sehen, wie dies in Form eines Programmiercodes funktioniert.

Jetzt wollen wir einmal ein Fazit von unserem kleinen Exkurs ziehen: Jeder Indikator verfügt über die folgenden Parameter:

  • Ein oder mehrere Datenpuffer (wenn auch nicht notwendigerweise) für die Anzeige seiner Werte oder für Zwischenberechnungen. Jeder einzelne Puffer verfügt wiederum über seine eigenen Parameter, welche definieren, auf welche Art und Weise er gezeichnet werden wird und ob die Werte überhaupt gezeichnet werden sollen. Ein Beispiel: Zeichnen des Indikators in Form eines Histogramms, eines Symbols oder einer Linie; verwende dazu die folgende Farbe und den folgenden Stil;
  • Wo der Indikator gezeichnet werden sollte (in einem Chart-Fenster oder einem Unterfenster);
  • Falls der Indikator in einem Unterfenster gezeichnet werden soll, sollten wir die Bandbreite manuell limitieren, oder sollte die Skalierung automatisch erfolgen.

Stellen Sie bitte sicher, dass Sie all diese Parameter vollständig kennen und verstehen. Jetzt werden wir einen Assistenten benutzen, um einen individuellen Indikator zu erstellen.


Die Erstellung eines individuellen Indikators

Starten Sie den MetaEditor, wählen Sie Datei-> Neu:



Jetzt werden wir ein Fenster mit dem „Expert Advisor“-Assistenten zu sehen bekommen; wählen Sie jetzt Individueller Indikator, und klicken Sie dann auf Weiter:



Füllen Sie die Felder Name, Autor und Link aus. Bis jetzt ist alles wie gewohnt, aber Sie können zusätzlich weitere Parameter hinzufügen. Welche Parameter könnten das sein?

Die Parameter sind allgemein gebräuchliche Variablen, die durch einen Benutzer individuell festgelegt werden können. Und ganz wichtig: Diese Variablen können im Rahmen eines Indikator-Codes verwendet werden. Der Sinn und Zweck dieser Parameter ist offensichtlich: Sie versetzen den Benutzer in die Lage, einige Aspekte der Arbeit der Indikatoren zu personalisieren. Sie können hierbei sämtliche denkbaren Parameter verwenden. Beispielsweise können Sie den zu verwenden Zeitraum festlegen, die Betriebsart, die Anzahl der Balken für die Mittelwertbildung usw.

Als praktisches Anwendungsbeispiel wollen wir nun einmal versuchen, einen Parameter hinzuzufügen, der uns die Anzahl der für die Berechnung der Indikatorwerte verwendeten Balken anzeigen soll. Für welches Anwendungsszenario könnte dies von Nutzen sein? Stellen Sie sich einmal vor, dass Ihr Indikator Ihre Systemressourcen über Gebühr beansprucht, weil er zu viele Berechnungen anstellt. Und darüber hinaus verändern Sie oftmals den Zeitrahmen des Charts, und Sie sehen sich dabei lediglich die letzten 100-200 Balken an. In diesem Fall benötigen Sie nicht die weiteren Berechnungen – diese verschwenden lediglich wertvolle Systemressourcen. Dieser Parameter wird Ihnen also in einer solchen Situation behilflich sein. Natürlich werden wir unseren Indikator möglichst einfach halten, sodass wir keine wertvollen Systemressourcen verschwenden. Dies stellt lediglich eine Variante für die Benutzung eines Indikatorparameters dar.

Um also einen neuen Parameter hinzuzufügen, klicken Sie auf Hinzufügen (1). Danach haben Sie die Möglichkeit, den Namen der Variable zu verändern (2). In unserem Falle ersetzen wir den vorgegebenen Namen mit barsToProcess. Sie können ebenfalls den Anfangswert verändern (3), d.h. den voreingestellter Startwert. Ändern Sie ihn auf 100. Darüber hinaus können Sie den Variablentyp verändern – aber in unserem Falle müssen wir nichts verändern, weil dieser Typ nämlich bereits ideal auf unsere Anforderungen passt. Nachdem sämtliche notwendigen Veränderungen vorgenommen wurden, klicken Sie auf Weiter:



Nun sind wir schon fast fertig. Jetzt müssen Sie nur noch angeben, wie der Indikator gezeichnet werden sollte: In einem separaten Fenster oder in einem Chart-Fenster. Sie können ebenfalls die Bandbreite limitieren. Überprüfen Sie den Indikator in einem separaten Fenster. Untenstehend sehen Sie ein leeres Feld mit dem Namen Indexe (Datenpuffer). Hier können Sie benötigte Anzahl von Datenpuffern festlegen (maximal 8). Darüber hinaus können Sie auch zu einem späteren Zeitpunkt noch weitere Puffer hinzufügen oder unbenötigte Puffer löschen, indem Sie den Code nachträglich verändern. Klicken Sie auf Hinzufügen, um einen Datenpuffer hinzuzufügen. Jetzt können Sie die Art und Weise verändern, auf die dieser Datenpuffer gezeichnet wird: Eine Linie, ein Histogramm, eine Sektion, oder ein Pfeil. Wir werden jetzt allerdings nichts verändern, und unser voreingestellter Typ der Darstellung ist also die Linie. Wählen Sie anschließend noch die gewünschte Farbe, und klicken Sie auf OK.

Jetzt ist Ihr erster Indikator endlich fertig programmiert! Nun ja – er zeichnet zwar noch überhaupt nichts, aber es handelt sich bereits um richtigen Code! Die Datei mit dem Quellcode befindet sich in dem Verzeichnis mit den Indikatoren: MetaTrader4\experts\indicators.


Jetzt wollen wir einmal jede einzelne Zeile analysieren.

Werfen wir also einen Blick darauf, was Meta Editor erstellt hat:

//+------------------------------------------------------------------+
//|                                             myFirstIndicator.mq4 |
//|                                                     Antonuk Oleg |
//|                                                   banderass@i.ua |
//+------------------------------------------------------------------+

Wie üblich besteht der Kopf aus einzeiligen Kommentaren, die sämtliche Informationen enthalten, welche Sie zuvor eingegeben haben. Weiter:

#property copyright "Antonuk Oleg"

Erinnern Sie sich noch an die Vorverarbeitungs-Anweisung #define aus unserem zweiten Artikel? Wir haben sie dort dafür benutzt, um Konstanten festzulegen. Hier fungiert sie als eine weitere Anweisung, die dazu dient, spezifische Eigenschaften eines Indikators festzulegen. In unserem Falle wird sie dafür benutzt, um die Autorenschaft anzugeben. Bitte beachten Sie hierbei, dass diese Linie mit dem speziellen Symbol # beginnt, gefolgt vom Schlüsselwort property (ohne Leerzeichen). Dann folgt eine konkrete Eigenschaft, die wir festlegen wollen – in unserem Falle handelt es sich um das Copyright, und dann schließlich der Wert dieser Eigenschaft. In unserem Fall ist dies eine Linie mit Ihrem Namen. Unter Verwendung der Anweisung #property können Sie sämtliche spezifischen Aspekte eines Indikators festlegen. Sie werden dies jetzt gleich sehen. All diese Eigenschaften werden als Voreinstellungen festgelegt werden. Lassen Sie uns nun weitermachen:

#property link      "banderass@i.ua"

Diese Anweisung zeigt uns, wie wir den Autor kontaktieren können. Sie werden nun vielleicht die Frage stellen, wo sich diese Daten (der Name des Autors und seine Kontaktdaten) befinden, weil sie nirgends angezeigt werden. Aber sie sind dennoch in der ausführbaren Datei enthalten. Und wenn Sie sich die ausführbare Datei als reinen Text anzeigen lassen, dann werden Sie diese Daten auch angezeigt bekommen:

Weiter:

#property indicator_separate_window

Diese Anweisung zeigt uns, dass der Indikator in einem separaten Unterfenster gezeichnet werden muss. Wie Sie sehen können, gibt es hierbei keine zusätzlichen Parameter – im Gegensatz zur vorhergehenden Anweisung.

#property indicator_buffers 1

Diese Anweisung legt fest, wie viele Datenpuffer zusammen mit dem Indikator verwendet werden sollen. Sie haben mittlerweile vielleicht festgestellt, dass diese Anweisungen in gewisser Weise den allgemeinen Funktionen ähneln: Sie verfügen ebenfalls über bestimmte Parameter, und sie arbeiten mit diesen. Allerdings gibt es hierbei einen wichtigen Unterschied: Sie werden nämlich in der ersten Instanz (d.h. noch vor der Kompilierung) ausgeführt.

#property indicator_color1 DarkOrchid

Geben Sie die Standardfarbe für den ersten Datenpuffer ein. Beachten Sie hierbei, dass die Nummerierung der Datenpuffer bei eins beginnt, und nicht bei null. Merken Sie sich das bitte, damit es in Zukunft zu keinerlei Problemen kommt. Die Eingabe der Farbe erfolgt mithilfe der vielen voreingestellten Farbbezeichnungen. Sie können die Schlüsselwörter für sämtliche verfügbaren Farben in der Hilfedatei nachlesen: MQL4 Referenz-> Standardkonstanten-> Web-Farben . In ähnlicher Weise können Sie die Farben anderer Datenpuffer festlegen, indem Sie ganz einfach die Nummer des jeweiligen Puffers eingeben.

extern int       barsToProcess=100;

Dies ist unser Parameter für den Indikator. Wir haben ihn mithilfe des Assistenten eingegeben. Beachten Sie bitte, dass der einzige Unterschied zu einer gewöhnlichen Variable in der Verwendung des Schlüsselwortesextern vor der Angabe des Variablentyps besteht. Und so sieht der Parameter für einen Benutzer aus, wenn dieser den Indikator startet:



Weiter:

double ExtMapBuffer1[];

Dies ist eine ganz normale Matrix. Aber die Dimensionsalität ist noch nicht angegeben, und die Initialisierung wurde noch nicht vorgenommen. Diese Matrix wird später als Datenpuffer eingerichtet werden.

Danach werden wir die einzelnen Funktionen deklarieren und beschreiben. Im Gegensatz zu einem normalen Skript verfügt jeder Indikator über drei Funktionen, anstelle einer Einzigen:

  • init() - Diese Funktion wird durch das Terminal lediglich ein einziges Mal aufgerufen, und zwar dann, wenn wir den Indikator starten. Ihr Zweck besteht darin, den Indikator für die Aufnahme seiner Arbeit vorzubereiten, die Datenpuffer anzulegen, die Parameter zu überprüfen (also das, was ein Benutzer selbst geschrieben hat), sowie andere vorbereitende Arbeiten durchzuführen. Diese Funktion ist nicht unbedingt notwendig. Wenn Sie keinen speziellen Code in ihr ausführen möchten, dann können Sie diese Funktion auch löschen.
  • deinit() - Diese Funktion wird ebenfalls nur ein einziges Mal aufgerufen, und zwar dann, wenn Sie einen Indikator aus einem Chart löschen. Sie sollten einen Indikator unbedingt für die Beendigung seiner Arbeit vorbereiten. Beispielsweise werden so geöffnete Dateien geschlossen, und graphische Objekte werden aus der Datei gelöscht (keine Sorge, wir werden Ihnen zeigen, wie das funktioniert). Diese Funktion ist ebenfalls nicht unbedingt notwendig.
  • start() - Im Gegensatz zu Skripten wird diese Funktion bei den Indikatoren bei jeden einzelnen Tick aufgerufen, d.h. immer dann, wenn neue Kurse für ein Währungspaar hereinkommen, mit dem Sie den Indikator verbunden haben, wird diese Funktion aufgerufen werden. Darüber hinaus wird diese Funktion beim Start des Indikators aufgerufen, das heißt unmittelbar nach der Funktion init().

Jetzt wollen wir mal sehen, was bei jeder einzelnen Funktion abläuft:

int init()
{
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   return(0);
}

Hier sehen wir den Aufruf von zwei wichtigen Funktionen für die Einrichtung eines Datenpuffers:

SetIndexStyle(0,DRAW_LINE);

Diese Funktion legt fest, auf welche Art und Weise die Datenpuffer gezeichnet werden. Der erste Parameter gibt hierbei an, auf welchen Datenpuffer die Veränderung angewendet werden sollte. Bitte beachten Sie, dass bei dieser Funktion (und bei anderen ähnlichen Funktionen) die Nummerierung der Datenpuffer bei 0 beginnt, und nicht bei eins, wie bei den Anweisungen. Dies ist ein wichtiger Umstand – passen Sie also gut auf. Der zweite Parameter gibt an, wie der ausgewählte Datenpuffer gezeichnet werden soll. In unserem Fall verwenden wir die Konstante DRAW_LINE, die anzeigt, dass der Datenpuffer als eine Linie gezeichnet werden wird. Natürlich gibt es auch noch weitere Konstanten, aber wir werden diese später behandeln.

SetIndexBuffer(0,ExtMapBuffer1);

Diese Funktion " verbindet " eine Matrix mit einer bestimmten Datenpuffer-Nummer. D.h., dass sie anzeigt, dass der Puffer mit der angegebenen Nummer die angegebene Datenmatrix zur Speicherung seiner Daten verwenden wird. Wenn man also die Elemente dieser Matrix verändert, verändert man auch den Wert des Datenpuffers. Tatsächlich ist eine Matrix im Grunde genommen auch nichts anderes als ein Datenpuffer. Das erste Argument ist der Name der Matrix, welche verbunden werden sollte.

return(0);

Ende der Funktion, Meldung null – die Initialisierung verlief erfolgreich.

int deinit()
{
//----
   
//----
   return(0);
}

Die Funktion der Deinitialisierung ist standardmäßig leer.

int start()
{
   int counted_bars=IndicatorCounted();
//----
   
//----
   return(0);
}

Jetzt kommt die wichtigste Funktion, denn der Hauptcode befindet sich hier. Beachten Sie bitte Folgendes: Die Variable counted_bars wird vorab deklariert. Sie wird durch die Funktion IndicatorCounted() initialisiert. Diese Variable wird üblicherweise

für die Optimierung und Beschleunigung des Arbeitsprozesses des Indikators verwendet – wir werden dies später analysieren. Und jetzt wollen wir einmal etwas in das Indikator-Fenster zeichnen.


Fertigstellung des Indikators

Lassen Sie uns einmal darüber nachdenken, was alles angezeigt werden sollte. Was wird der Indikator zeigen? Etwas ganz Einfaches. Zunächst wollen wir einmal Zufallszahlen zeichnen lassen. Und warum auch nicht? Dies garantiert uns mit einer Wahrscheinlichkeit von 50 % Gewinnsignale.

Lassen Sie uns in unserer Funktion init() einen Code zur Initialisierung des Generators für die Zufallszahlen implementieren:

int init()
{
 
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
 
   // initialization of the generator of random numbers
   MathSrand(TimeLocal());
 
   return(0);
}

Die Initialisierung ist erledigt – jetzt folgt die Funktion start():

int start()
{
   int counted_bars=IndicatorCounted();
 
   for(int i=0;i<Bars;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Kompilierung - F7. Starten Sie das Terminal und finden Sie das Bedienfeld Navigator; wählen Sie dann die Sektion Individuelle Indikatoren und führen Sie einen Doppelklick auf den Namen unseres Indikators aus:

Der Indikator wird daraufhin an den aktiven Chart angehängt werden :

Sie sehen, dass alles funktioniert. Jetzt wollen wir uns einmal ansehen, was genau dieser Code eigentlich tut:

for(int i=0;i<Bars;i++)

Wir benutzen die Schleife for, um durch alle Elemente des Datenpuffers durchzugehen. Weil zu jedem einzelnen Element des Datenpuffers ein bestimmter Balken gehört, benutzen wir diese Schleife, und wir beginnen bei dem Balken mit der Nummer null (dem letzten verfügbaren Balken) und beenden unsere Arbeit mit dem ersten verfügbaren Balken, dessen Nummer um eins niedriger ist als die Variable Balken (weil wir die Balken von null ausgehend zählen).

{
   ExtMapBuffer1[i]=MathRand()%1001;
}

Mit jeder Wiederholung erhöht sich der Zähler um eins, und wir bewegen uns vom letzten verfügbaren Balken hin zum ersten Balken – und gleichzeitig ordnen wir jedem einzelnen Element im Datenpuffer (jedes davon korrespondiert mit einem bestimmten Balken) eine Zufallszahl im Bereich von 0 bis 1000 zu. Falls es Ihnen schwer fallen sollte, zu verstehen, auf welche Art und Weise ein bestimmtes Element im Datenpuffer mit einem bestimmten Balken verbunden ist, dann versuchen Sie doch einmal, die Schleife auf die folgende Art und Weise zu verändern. Sehen Sie sich dann das Resultat im Terminal an:

for(int i=0;i<Bars;i++)
{
   ExtMapBuffer1[i]=i;
}

Jetzt wird der Indikator die Nummer jedes einzelnen Balken anzeigen, siehe hier:



Wie Sie sehen können, erhöht sich die Balken-Nummer vom letzten Balken ausgehend bis hin zum ersten Balken (von 0 bis hin zu xx Balken). Ich hoffe, dass Sie jetzt den Zusammenhang zwischen den Elementen im Datenpuffer und den Balken auf dem Chart verstehen.

Lassen Sie uns jetzt zum Code des " Zufalls-" Indikators zurückkehren. Wenn Sie diesen Indikator für ein paar Minuten benutzen, dann werden Sie sehen, dass jeder einzelne Indikator-Tick eine völlig unterschiedliche Chartbewegung auslöst. D.h., dass jeder einzelne Tick eine völlige Neukalkulation dessen darstellt, was beim vorhergehenden Tick berechnet wurde. Dies ist für uns äußerst unpraktisch, weil wir so überhaupt nicht sehen können, was vor nur einem Tick passiert ist. Aber das spielt keine Rolle, weil in der Praxis niemand einen solchen Indikator verwenden würde – wir benutzen ihn nur als ein Beispiel, um die Erstellung von Indikatoren zu erlernen. Aber es gibt noch etwas, worauf ich Sie aufmerksam machen möchte: Stellen Sie sich einmal vor, dass Ihr Indikator eine Menge von komplexen Berechnungen anstellt – und die Berechnung jedes einzelnen Balken beansprucht große Prozessorressourcen. In diesem Fall wird immer dann, wenn ein neuer Kurs erscheint, Ihr Indikator den Wert für jeden einzelnen verfügbaren Balken neu berechnen – selbst dann, wenn diese Berechnung bereits vorher erfolgt ist. Ist Ihnen das klar? Statt also den Wert eines Balkens nur einmal zu berechnen, wird dieser Wert immer und immer wieder neu berechnet werden. Die Eliminierung solcher fehlerhafte Programmierungen, die zur Verschwendung von Systemressourcen führen, bezeichnet man als Optimierung.

Wie können wir also dieses Problem lösen? Normalerweise geschieht dies wie folgt: Zunächst wird ein Indikator mithilfe sämtlicher verfügbarer Kerzen auf dem Chart kalkuliert. Sobald dann neue Kurse hereinkommen, wird lediglich der Wert für die letzte Kerze auf dem Chart neu berechnet werden. Dies ist die einzig vernünftige Vorgehensweise – hierbei kommt es zu keinerlei unnützen Berechnungen. Lassen Sie uns also nun die Funktion start() optimieren, sodass sie in der folgenden Art und Weise arbeitet:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Wir wollen nun jede einzelne Zeile analysieren:

int counted_bars=IndicatorCounted(),

Wir deklarieren die Variable counted_bars, welche die Anzahl der Balken speichern wird, welche durch den Indikator berechnet werden. Tatsächlich gibt die Funktion IndicatorCounted() die Anzahl der unveränderten Balken nach dem vorhergehenden Aufruf der Funktion start() aus. Wenn also der erste Aufruf der Funktion start() erfolgt ist, wird die Funktion IndicatorBars() Folgendes ausgeben: 0. Dies liegt daran, dass bis jetzt alle Balken neu für uns sind. Wenn es sich allerdings nicht um den ersten Aufruf handelt, und lediglich der letzte Balken verändert wurde, dann wird die Funktion IndicatorBars() eine Zahl ausgeben, die Bars-1 entspricht.

limit;

Hier ist eine weitere Variable, welche als Begrenzer fungiert. D.h., dass sie die schnellere Abarbeitung der Schleife ermöglichen wird, indem bereits zuvor berechnete Kerzen weggelassen werden.

   if(counted_bars>0)
      counted_bars--;

Wie wir bereits oben festgestellt haben, gilt, dass in dem Falle, dass die Funktion IndicatorCounted() als Ergebnis 0 liefert, die Funktion start() zum ersten Mal aufgerufen wurde, und sämtliche Balken sind daher " neu " für uns (d.h., dass der Indikator für all diese Balken bisher noch nicht kalkuliert wurde). Wenn es sich allerdings nicht um den ersten Aufruf der Funktion start() handelt, dann wird ein Wert ausgegeben werden, welcher Balken-1 entspricht. Diese Kondition dokumentiert also solch eine Situation. Im Anschluss daran verringern wir die Variable counted_bars um 1. Lediglich der letzte Balken kann verändert werden – warum tun wir dies also? Die Sache ist wie folgt: Es gibt einige Situationen, in denen der letzte Tick des vorhergehenden Balkens unbearbeitet bleibt, weil zum zum Zeitpunkt des Eintreffens des letzten Ticks die Bearbeitung des vorletzten Ticks noch nicht abgeschlossen war. Und aus diesem Grunde wurde der individuelle Indikator nicht aufgerufen, und seine Berechnung ist daher nicht erfolgt. Dies ist der Grund, weshalb wir die Variable counted_bars um 1 erniedrigen, um diese Situation aufzulösen.

limit=Bars-counted_bars;

Hier ordnen wir der Variable limit (also dem Begrenzer) die Nummern der letzten Balken zu, welche noch berechnet werden müssen. Während die Variable counted_bars die Anzahl der bereits berechneten Kerzen abspeichert, stellen wir lediglich die Differenz zwischen den Balken (der gesamten Anzahl der verfügbaren Balken) und den counted_bars fest, um zu definieren, wie viele Kerzen berechnet werden müssen.

for(int i=0;i<limit;i++)
{
   ExtMapBuffer1[i]=MathRand()%1001;
}

An der Schleife selbst hat sich fast nichts verändert. Wir haben lediglich die Bedingung der Implementierung verändert . Jetzt wird die Schleife abgearbeitet werden, wenn der Zähleri einen geringeren Wert als limit aufweist.

Der Prozess der Optimierung ist jetzt erledigt. Wenn Sie sich einmal die aktualisierte Version des Indikators anschauen, dann werden Sie sehen, dass immer dann, wenn ein neuer Tick empfangen wird, sich lediglich der Wert des letzten Balkens verändert. Sie sollten stets versuchen, eine solche Optimierung vorzunehmen – selbst dann, wenn ein Indikator lediglich einfache Berechnungen vornimmt. Dies ist einfach die korrekte Vorgehensweise.

Erinnern Sie sich noch an den Indikator-Parameter barsToProcess, den wir zum Assistenten hinzugefügt haben? Jetzt ist der Zeitpunkt gekommen, um ihn einzusetzen. Wir müssen hierzu lediglich ein paar Programmzeilen am Anfang unserer Schleife hinzufügen:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      ExtMapBuffer1[i]=MathRand()%1001;
   }
   
   return(0);
}

Sie sehen schon, dass das alles im Prinzip ganz einfach ist. Wir überprüfen, ob der Wert von limit höher ist als barsToProcess. Falls ja, dann wird der Begrenzer durch die Zuordnung erniedrigt werden. Wenn wir in der Folge barsToProcess=100 definieren, dann werden wir als Ergebnis ein Bild wie dieses zu Gesicht bekommen:

Sie sehen, dass lediglich die Anzahl von Balken, welche wir definiert haben, berechnet wird.

Unser Indikator ist jetzt fast fertig. Dennoch verfügen wir noch nicht über klare Signale für den Zeitpunkt des Einstiegs in den Markt. Wir benötigen also einen höheren Sicherheitsfaktor. Zu diesem Zwecke werden wir Level verwenden.

Level sind horizontale Linien, welche durch den Indikator unter Verwendung eines bestimmten Stils, einer bestimmten Farbe und einer bestimmten Breite gezeichnet werden. Hierzu gilt es anzumerken, dass die maximale Anzahl der Level für einen Balken 8 beträgt. Darüber hinaus können Sie Level setzen, indem Sie Anweisungen oder Funktionen verwenden. Es ist empfehlenswert, die erste Variante zu benutzen, wenn Sie die Level als Standard festlegen möchten. Für die dynamische Veränderung der Level während der Arbeit des Indikators sollten Sie dagegen Funktionen benutzen. Wir wollen also zwei Level definieren: Das erste Level sollte beim Punkt 800 beginnen, und das zweite Level beim Punkt - 200. Zu diesem Zwecke wollen wir nun mehrere Anweisungen zu Beginn des Indikator-Codes hinzufügen:

//+------------------------------------------------------------------+
//|                                             myFirstIndicator.mq4 |
//|                                                     Antonuk Oleg |
//|                                                   banderass@i.ua |
//+------------------------------------------------------------------+
#property copyright "Antonuk Oleg"
#property link      "banderass@i.ua"
 
#property indicator_level1 800.0
#property indicator_level2 200.0
#property indicator_levelcolor LimeGreen
#property indicator_levelwidth 2
#property indicator_levelstyle 0
 
#property indicator_separate_window

Lassen Sie uns die neuen Anweisungen nun einmal analysieren:

#property indicator_level1 800.0

Diese Anweisung zeigt uns, dass das Level 1 beim Punkt 800,0 platziert sein sollte. Achten Sie bitte darauf, dass die Nummerierung der Datenpuffer mit 1 beginnt, genau wie bei den Anweisungen für die Definition der Datenpuffer. Um ein weiteres Level festzulegen ändern Sie ganz einfach die Nummer des Levels am Ende der Anweisung:

#property indicator_level2 200.0

Bei der Festlegung der externen Form der Level gilt es einige wichtige Beschränkungen zu beachten. Sie können nicht jedes einzelne Level individuell definieren. Sämtliche Einstellungen werden ohne Ausnahme auf alle Level gleichzeitig angewendet. Wenn Sie ein einzelnes Level individuell definieren möchten, dann sollten Sie stattdessen Objekte verwenden (und auf den Einsatz von Leveln vollständig verzichten) – wir werden dies im Rahmen des nächsten Artikels erklären.

#property indicator_levelcolor LimeGreen

Diese Anweisung legt die Farbe fest, die zum Zeichnen sämtlicher Level verwendet werden wird.

#property indicator_levelwidth 2

Diese Anweisung legt die Breite der Linien fest, mit denen sämtliche Level gezeichnet werden. Sie können diese Breite von 1 bis hin zu 5 frei wählen. Denken Sie daran, dass im Falle, dass eine Breite größer als 1 ist, sämtliche Level in Form einer durchgehenden Linie gezeichnet werden. Falls Sie zum Zeichnen der Level einen anderen Stil verwenden möchten, dann benutzen Sie bitte nur die Breite 1.

#property indicator_levelstyle STYLE_SOLID

Diese Anweisung legt den Stil zur Zeichnung der Linien fest. Es gibt hierzu die folgenden voreingestellten Konstanten:

  • STYLE_SOLID - eine durchgehende Linie
  • STYLE_DASH - eine gestrichelte Linie
  • STYLE_DOT - eine gepunktete Linie
  • STYLE_DASHDOT - eine gestrichelte und gepunktete Linie
  • STYLE_DASHDOTDOT - eine gestrichelte und gepunktete Linie mit doppelten Punkten


Wir haben nun die Entwicklung unseres " Zufalls-" Indikators abgeschlossen. Jetzt wollen wir die Quelldatei mit einem treffenderen Namen abspeichern - randomIndicator.mq4. Kompilieren Sie die Quelldatei hierzu noch einmal neu. Dieser Indikator wird auch im folgenden Abschnitt verwendet werden. Die endgültige Version sollte dann wie folgt aussehen:



Die Funktion iCustom

Wir wollen uns nun einer weiteren äußerst nützlichen Funktion zuwenden: iCustom. Sie dient dazu, Werte von einem beliebigen individuellen Indikator zu erhalten. Erinnern Sie sich hierzu daran, dass wir für sämtliche eingebauten Indikatoren Funktionen benutzen, um mit den technischen Indikatoren zu arbeiten, welche wir im vorhergehenden Artikel beschrieben haben (beispielsweise iADX(), iMACD etc.). Für alle anderen Indikatoren (d.h. für individuelle Indikatoren) benutzen wir die Funktion iCustom. Diese Funktion ist universell einsetzbar, und sie kann in Verbindung mit jedem individuellen Indikator verwendet werden, welcher die folgenden Voraussetzungen erfüllt:

  • Der Indikator ist bereits kompiliert und liegt in Form einer ausführbaren Datei vor (*.ex4)
  • Der Indikator befindet sich im Verzeichnis MetaTrader 4\experts\indicators

Der Prototyp der Funktion sieht wie folgt aus:

double iCustom( string symbol, int timeframe, string name, ..., int mode, int shift);

Parameter:

  • Symbol: Definiert, welches Finanzhandelsinstrument (Währungspaar) zur Berechnung von individuellen Indikatorwerten verwendet werden sollte. Benutzen Sie NULL (oder 0), wenn Sie das aktuelle (aktive) Finanzhandelsinstrument (bzw. dessen Chart) verwenden möchten.
  • timeframe: Definiert, auf welchen Zeitraum (Periode) der Indikator angewandt werden sollte. Setzen Sie 0 für den aktuellen Zeitraum, oder eine der folgenden Konstanten (PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1).
  • name: Der Name der ausführbaren Datei des individuellen Indikators. Sie sollten nur den Namen angeben: Geben Sie nicht die Erweiterung an (.ex4), und auch nicht den Pfad zur Datei (experts/indicators/). Wenn der Name der ausführbaren Datei Ihres individuellen Indikators beispielsweise "RandomIndicator.ex4", ist, dann schreiben Sie "RandomIndicator". Die Groß- und Kleinschreibung ist hierbei nicht von Relevanz. Es bedeutet, dass Sie "RANDOMindicator" schreiben können, und es wird trotzdem funktionieren.
  • ...: Hier sollten Sie sämtliche Werte definieren, die für die Parameter Ihres individuellen Indikators gelten. Im Beispiel unseres Indikators RandomIndicator gibt es lediglich einen Parameter – barsToProcess. D.h., dass wir in diesem Falle an dieser Stelle 100 angeben (oder irgendeine andere Zahl, die sich für Ihren Wert eignet). Falls die Anzahl der Parameter allerdings zwei oder mehr betragen sollte, dann werden die Werte dafür in derselben Reihenfolge angegeben, wie sie auch im individuellen Indikator deklariert sind; die Trennung erfolgt hierbei durch Kommata. Jetzt werden wir einmal versuchen, einen Indikator zu erstellen, die auf diese Funktion basiert – danach werden Sie all diese Erklärungen besser verstehen.
  • mode: Der Betriebsmodus des individuellen Indikators. Tatsächlich stellt dies die Anzahl der Datenpuffer dar, also des Wertes, den Sie erhalten möchten. Die Nummerierung beginnt hierbei bei null (im Gegensatz zu den Anweisungen). Falls der individuelle Indikator lediglich über einen Datenpuffer verfügen sollte, dann sollte dieser Parameter stets auf 0 gesetzt werden.
  • shift: Definiert, auf welchen Balken der individuelle Indikator angewandt werden sollte.

Beispiele für die Verwendung:

ExtMapBuffer[0]=iCustom(NULL,PERIOD_H1,"Momentum",14,0,0);
 
// assign to the first element of the array ExtMapBuffer the value of the custom 
// indicator Momentum on the last available bar. We use here the active 
// security on hour chart. The name of the executable file: Momentum. 
// This indicator has only one parameter - period. In our case the period 
// is equal to 14. This indicator has only one data buffer, so we use zero, 
// in order to get access to its values.

double signalLast=iCustom("EURUSD",PERIOD_D1,"MACD",12,26,9,1,0);
 
// declare a new variable signalLast and assign to it the value of the custom 
// indicator индикатора MACD on the last available bar. We use the pair EURUSD on 
// a daily chart. The name of the executable file: MACD. This indicator has 3 parameters: 
// period for quick average, period for slow average and period for a signal line. 
// This indicator also has 2 data buffers. The first one is with values of the main line. The second one 
// with values of a signal line. In our case we take the value of the signal line.

Signal-Indikator

Jetzt wollen wir noch einen weiteren einfachen Indikator erstellen. Stellen Sie sich hierzu die folgende Situation vor: Sie haben einen ziemlich komplexen Indikator erstellt, der über mehrere Datenpuffer verfügt. Viele von diesen Puffern werden in einem separaten Fenster angezeigt, und weitere Puffer werden für Zwischenberechnungen verwendet. Sie kennen die Signale zum Kauf und Verkauf genau. Allerdings liegt das Problem darin, dass es sehr schwierig ist, diese Signale zu erkennen. Sie müssen dazu konstant auf Ihren Monitor starren und versuchen, Kreuzungspunkte zu identifizieren, die über oder unter Ihren Levels liegen. Aus diesem Grunde beschließen Sie, einen oder mehrere Indikatoren zu erstellen, welche Ihnen diese Arbeit abnehmen könnten und Ihnen lediglich die Signale zum Einstieg anzeigen würden. Beispielsweise könnte es sich hierbei um Pfeile handeln, welche Ihnen anzeigen, in welche Richtung Sie Positionen eröffnen sollten. Dies ist lediglich ein theoretisches Anwendungsszenario, für das sich ein Signal-Indikator eignen könnte. Unsere Situation ist viel einfacher – aber dennoch ähnelt sie der oben geschilderten Situation.

Wir werden nun einen Signal-Indikator schreiben, der auf den zuvor präsentierten Indikator RandomIndicator basiert. Zunächst müssen wir hierzu die Einstiegskonditionen definieren - und dazu wiederum benötigen wir unsere Level. Die Einstiegskonditionen werden die Folgenden sein:

  • Wenn eine Linie sich über die Obergrenze (800,0) bewegt, dann gilt es, zu kaufen.
  • Wenn eine Linie sich unter die Untergrenze (200,0) bewegt, dann gilt es, zu verkaufen.

Jetzt ist es höchste Zeit, einen neuen Indikator zu erstellen. Benutzen Sie hierzu den „Expert Advisor“-Assistenten, um einen neuen individuellen Indikator anzulegen. Fügen Sie einen zusätzlichen Parameter hinzu, genau wie im vorherigen Fallbeispiel:

Der letzte Schritt (die Definition der Zeichen-Eigenschaften des individuellen Indikatorprogramms) sollte jetzt der Folgende sein:



Fügen Sie zunächst zwei Datenpuffer hinzu, welche für das Zeichnen der Signale zum Kauf und Verkauf in Form von Pfeilen verwendet werden sollen. Ändern Sie den Typ der Datenpuffer auf Pfeil. Ändern Sie die Farben und die Symbol-Codes. Untenstehend finden Sie sämtliche verfügbaren Symbol-Codes:

Wir müssen den Indikator nicht unbedingt in einem separaten Fenster zeichnen, weil wir nämlich die Signale im Chart-Fenster zeichnen werden.

Wir benutzen zwei Datenpuffer, weil es uns mit nur einem Puffer nicht möglich sein wird, verschiedene Pfeile (Symbole) zu zeichnen. Jeder einzelne Datenpuffer, der in Form von Symbolen angezeigt wird, kann lediglich mithilfe eines einzigen Symbols gezeichnet werden. Lassen Sie uns jetzt den Initialisierung-Code des Indikators aufmerksam analysieren:

int init()
{
//---- indicators
   SetIndexStyle(0,DRAW_ARROW);
   SetIndexArrow(0,236);
   SetIndexBuffer(0,ExtMapBuffer1);
   SetIndexEmptyValue(0,0.0);
   SetIndexStyle(1,DRAW_ARROW);
   SetIndexArrow(1,238);
   SetIndexBuffer(1,ExtMapBuffer2);
   SetIndexEmptyValue(1,0.0);
//----
   return(0);
}

Beachten Sie bitte, dass jetzt eine andere Konstante für den Typ der Zeichnung des Datenpuffers verwendet wird - DRAW_ARROW:

SetIndexStyle(0,DRAW_ARROW);

Wir sehen jetzt auch zwei neue Funktionen, die für die Einstellung des zu zeichnenden Symbols verwendet werden. SetIndexArrow dient dazu, einzustellen, welches Symbol zur Repräsentation eines Datenpuffers verwendet werden soll. Das erste Argument ist die Nummer des Datenpuffers, und das zweite Argument ist der Symbol-Code, welcher den Indikator repräsentieren wird:

SetIndexArrow(0,236);

SetIndexEmptyValue dient dazu, einen " leeren " Wert zu definieren. Dies bedeutet, dass wir den Wert angeben, bei welchem wir nichts zeichnen müssen. Dies ist in unserem Falle äußerst praktisch, weil die Signale in unserem Falle nicht für jeden Balken generiert werden. Es funktioniert auf die folgende Art und Weise: Wenn wir für den aktuellen Balken keine Matrix zeichnen müssen, dann ordnen Sie dem korrespondierenden Element im Datenpuffer ganz einfach einen " leeren " Wert zu – in unserem Falle ist dies die Null. Das erste Argument der Funktion ist die Nummer des Datenpuffers. Das zweite Argument ist der " leere " Wert:

SetIndexEmptyValue(0,0.0);

Der verbleibende Initialisierung-Code setzt weitere Datenpuffer, ganz ähnlich wie im Falle des " Zufalls-" Indikators, den wir weiter oben analysiert haben. Lassen Sie uns jetzt die Codes in der Funktion start() vervollständigen:

int start()
{
   int counted_bars=IndicatorCounted(),
       limit;
 
   if(counted_bars>0)
      counted_bars--;
   
   limit=Bars-counted_bars;
   
   if(limit>barsToProcess)
      limit=barsToProcess;
  
   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }
   
   return(0);
}

Der vollständige Code, bis die Schleife vom " Zufalls-"Indikator wiederholt wird. Tatsächlich ist dieser Code Standard für jeden Indikator, und er wiederholt sich immer wieder – mit geringfügigen Veränderungen. Lassen Sie uns jetzt die Schleife eingehend analysieren:

   for(int i=0;i<limit;i++)
   {
      double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
      
      if(randomValue>800.0)
         ExtMapBuffer1[i]=High[i]+5*Point;
      else
         ExtMapBuffer1[i]=0.0;
         
      if(randomValue<200.0)
         ExtMapBuffer2[i]=Low[i]-5*Point;         
      else
         ExtMapBuffer2[i]=0.0;         
   }

Zunächst deklarieren wir die Variable randomValue (Zufallswert) und ordnen diese dem Wert unseres " Zufalls-" Indikators auf dem aktuellen Balken zu. Zu diesem Zwecke verwenden wir die Funktion iCustom:

double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i);
 
// get the value of the "random" indicator on the i-th bar. Use the active chart on the current period. 
// The name of the executable file of indicator: RandomIndicator. Single parameter of "random" indicator
// is number of bars for calculation. In our indicator there is also analogous variable, that is why
// we use it. In "random" indicator only 1 data buffer, so we use 0, for getting
// access to its values.

Falls der Wert des " Zufalls-" Indikators oberhalb der Obergrenze (800) liegt, dann stellt dies ein Signal zum Kauf dar:

if(randomValue>800.0)
   ExtMapBuffer1[i]=High[i]+5*Point;

// if there is signal to buy, assign to current element of data buffer the highest
// value of the current bar. Besides add 5 points, so that the arrow were a little higher 
// than the current price. The predetermined variable Point is used to get automatically
// a multiplier for presenting points. Otherwise we would have to write something like
// this: ExtMapBuffer1[i]=High[i]+0.0005; 

Andernfalls, d.h. falls es kein Kauf-Signal geben sollte:

else
   ExtMapBuffer1[i]=0.0;
 
// if no Buy signal, assign to the current element of data
// buffer "empty" value, which is equal to 0.0.
// Now no symbol will be shown on this bar.

Falls der Wert des " Zufalls-" Indikators sich unterhalb der Untergrenze (200) befinden sollte, dann stellt dies ein Verkaufs-Signal dar:

if(randomValue<200.0)
   ExtMapBuffer2[i]=Low[i]-5*Point;
 
// if it is signal to sell, assign to the current element of data buffer the lowest
// value of the current bar. Besides diminish the value by 5 points, so that the arrow were 
// a little lower than the current price.

Andernfalls, d.h. falls es kein Verkaufs-Signal geben sollte:

else
   ExtMapBuffer2[i]=0.0;
 
// if no Sell signal, assign to the current element of data
// buffer "empty" value. Now no symbol will be shown on this bar.

Das war die Schleife. Kompilieren Sie den Indikator und starten Sie ihn im Terminal:





Über den Stil

Nein – hierbei handelt es sich nicht um Regeln zur Auswahl einer passenden Krawatte zu einem Hemd und einem Mantel, auch wenn das immer eine brennende Frage darstellt. Der Programmierstil ist sehr bedeutsam, wenn Sie Ihren Code nicht für sich allein schreiben wollen. Tatsächlich verfügt jeder Entwickler über seinen ganz eigenen Programmierstil. Jeder Entwickler entwirft seine Schleifen auf seine ganz eigene Art und Weise, er verwendet verschiedene Absätze (oder überhaupt keine Absätze), er deklariert Variablen etc. Sie sollten ihren eigenen Programmierstil entwickeln, den sie später bei all Ihren Projekten beibehalten. Ich möchte Ihnen hierzu ein paar Empfehlungen geben, die Ihnen später dabei helfen werden, Ihren Code so zu schreiben, dass er möglichst einfach zu lesen und zu verstehen ist:

  • Schreiben Sie nicht mehrere Prozesse in einer einzigen Zeile, die lediglich durch Strichpunkte (;) voneinander getrennt sind
  • Schreiben Sie die Namen der Variablen und Funktionen in englischer Sprache
  • Verwenden Sie bei den Namen der Variablen Großbuchstaben als Trennzeichen
  • Vermeiden Sie die übermäßige Verwendung von Abkürzungen und Akronymen bei der Benennung von Variablen und Funktionen
  • Fügen Sie regelmäßige Absätze von einer bestimmten Länge ein, um ebenmäßige, gleich lange Code-Abschnitte zu erhalten
  • Fügen Sie bei jedem neuen Abschnitt (von einer Schleife oder einer Kondition) zusätzliche Absätze ein
  • Gruppieren Sie Variablen desselben Typs
  • Erstellen Sie angemessene Kommentare für größere und schwierig zu verstehende Code-Blöcke
  • Schreiben Sie angemessene Kommentare für die Funktionen, welche Sie geschrieben haben (ihre Aufgabe, Parameter etc.)


Schlussbemerkung

Wir haben heute etwas Neues gelernt. Sie haben zwei einfache Indikatoren erstellt. Nun ja – diese sind zwar in der Praxis nutzlos, aber ich bringe Ihnen ja auch nicht bei, wie man erfolgreich handelt! Was Sie dagegen gelernt haben, ist, wie Indikatoren funktionieren, und welche Parameter und Eigenschaften sie haben. Sie haben gesehen, wie man Datenpuffer erstellt und mit ihnen arbeitet. Sie wurden mit mehreren neuen Funktionen vertraut gemacht. Die Funktion iCustom ist äußerst wichtig, und sie wird im weiteren Verlauf dieser Reihe sogar in "Expert Advisors" Verwendung finden. Falls Sie auf irgendwelche Schwierigkeiten stoßen sollten, dann lesen Sie sich diesen Artikel bitte nochmals durch, und versuchen Sie, das Problem selbstständig zu verstehen. Falls Sie danach immer noch irgendwelche Fragen haben sollten, dann zögern Sie bitte nicht, die Foren zu benutzen, oder schreiben Sie einen Kommentar zu diesem Artikel.