English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Erzeugung von Kursschwankungs-Indikatoren in MQL5

Erzeugung von Kursschwankungs-Indikatoren in MQL5

MetaTrader 5Indikatoren | 11 Januar 2016, 14:28
1 060 0
Denis Zyatkevich
Denis Zyatkevich

Einleitung

Beim Börsenhandel möchte man immer so viel Informationen wie möglich, um ein maximal detailliertes Gesamtbild der Kursveränderungen vor sich zu haben. Dazu kann man das Kursschwankungs-Chart verwenden. Und das wollen wir nun in MQL5 erzeugen.

In diesem Beitrag geht es um die Erzeugung von zwei Indikatoren: dem Kursschwankung-Indikator, der das Chart der Kursschwankungen des Kurses zeichnet und dem Kursschwankungs-"Kerzen" Indikator, der "Kerzen" mit der angegebenen Anzahl von Kursschwankungen zeichnet. Jeder dieser Indikatoren schreibt die eingehenden Kurse in eine Datei und verwendet die gespeicherten Daten dann nach einem Neustart des Indikators (diese Daten können auch von anderen Programmen verwendet werden).

Erzeugung eines Kursschwankungs-Indikators

Schreiben wir also einen Indikator in MQL5, der Kursschwankungsdaten auf ein Chart abbildet. Ein Beispiel eines solchen Indikators ist in Abb. 1 zu sehen:

Abb. 1 Beispiel eines Kursschwankung-Charts

Der Indikator zeichnet zwei Linien: Briefkurs und Geldkurs. In den Indikator-Optionen kann die Zeichnung jedes der beiden Kurses entsprechend abgestellt werden.

Der Indikator speichert die vom Makler erhaltenen Kurse des aktuellen Symbols in einer Textdatei im folgenden Format: Serverzeit, Briefkurs und Geldkurs:

2010.03.26 19:43:02 1.33955 1.33968

Der Dateiname entspricht dem Namen des Finanzinstruments (z.B. EURUSD.txt). Die Dateien finden sich im folgenden Pfad: MT5_Folder\MQL5\Files. Das zusätzliche Directory für eine Datei und ein Dateinamen-Präfix können ebenfalls in den Optionen des Indikators festgelegt werden (dies ist sinnvoll, wenn mehrere Indikatoren an die Charts mit dem gleichen Symbol angehängt sind).

Zur Erzeugung eines Indikators, startet man den MetaTrader5 Client-Terminal und den MetaQuotes Language Editor durch Drücken von "F4". Schreiben wir nun den Programmcode.

Wir legen hier fest, dass der Indikator in einem separaten Fenster unter dem Kurschart gezeichnet werden soll:

// indicator in a separate window
#property indicator_separate_window

Es sollten zwei Indikator-Linien (Briefkurs- bzw. Geldkurs) gezeichnet werden, also müssen wir zwei graphische Darstellungen verwenden:

// two graphic plots are used: for Bid and Ask lines
#property indicator_plots 2

Wir müssen zwei Indikator-Buffer festlegen, die die auf dem Chart darzustellenden Daten enthalten:

// two indicator's buffers
#property indicator_buffers 2

Also bestimmen wir für jede Indikatorzeile die Zeichnungsart DRAW_LINE (Linie), den Zeichnungsstil STYLE_SOLID (durchgehende Linie) und die Textmarkierungen "Brief" und "Geld":

// drawing type of a Bid line
#property indicator_type1 DRAW_LINE
// drawing color of a Bid line
#property indicator_color1 Red
// drawing style of a Bid line
#property indicator_style1 STYLE_SOLID
// text label of a Bid line
#property indicator_label1 "Bid"
// drawing type of an Ask line
#property indicator_type2 DRAW_LINE
// drawing color of an Ask line
#property indicator_color2 Blue
// drawing style of an Ask line
#property indicator_style2 STYLE_SOLID
// text label of an Ask line
#property indicator_label2 "Ask"

 Dann legen wir die Eingabe-Variablen fest, deren Werte vom Benutzer im Optionen-Menü des Indikators verändert werden können.

// the BidLineEnable indicates showing of a Bid line
input bool BidLineEnable=true; // Show Bid Line
// the AskLineEnable indicates showing of an Ask line
input bool AskLineEnable=true; // Show Ask Line
// the path_prefix defines a path and file name prefix
input string path_prefix=""; // FileName Prefix

Mit den BidLineEnable  und AskLineEnable  Variablen können Sie die Anzeige der Brief- und Geldlinien im Indikator aktivieren oder deaktivieren. Mit der path_prefix Variable können Sie das Präfix des Dateinamens festlegen, das sich vor dem Dateinamen befindet. Mit ihrer Hilfe lässt sich auch der Pfad für das Sub-Directory festlegen, z.B. lautet der Pfad für die Dateien bei path_prefix = "MyBroker/test_" wie folgt: Bei "MetaTrader5_Folder\MQL5\Files\MyBroker" ist der Dateiname für das Symbol "EURUSD" also "test_EURUSD.txt".

Auf globaler Ebene deklarieren wir die Variablen, die in verschiedenen Funktionen des Indikators verwendet werden. Die Werte dieser Variablen werden zwischen den Aufrufen des Indikators gespeichert:

// the tick_stored variable is a number of served quotes
int ticks_stored;
// the BidBuffer[] and AskBuffer[] arrays - are indicator's buffers
double BidBuffer[],AskBuffer[];

Die Variable tick_stored  wird zur Speicherung der Anzahl der verfügbaren Quoten verwendet. BidBuffer[] und AskBuffer[]  sind dynamische Arrays, die als Indikator-Buffer verwendet werden. Die Kursdaten, als Brief- und Geldlinien auf dem Chart dargestellt, sind in diesen Buffern abgelegt.

Die OnInit  Funktion gibt an, dass die BidBuffer[]  und AskBuffer[]  Arrays die Daten für die graphische Darstellung enthalten. Legen wir nun fest, dass Daten mit Werten des Indikator-Buffers, die Null entsprechen, nicht auf dem Chart gezeichnet werden sollen.

void OnInit()
  {
   // the BidBuffer[] is an indicator buffer
   SetIndexBuffer(0,BidBuffer,INDICATOR_DATA);
   // the AskBuffer[] is an indicator buffer
   SetIndexBuffer(1,AskBuffer,INDICATOR_DATA);
   // setting EMPTY_VALUE for a Bid line
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // setting EMPTY_VALUE for an Ask line
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
  }

Jetzt erzeugen wir die OnCalculate Funktion und führen alle Parameter auf, die bei Aufruf an die Funktion übertragen werden sollen: 

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

Deklarieren wir also die Variablen:

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, i is a loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,i;
// the last_price_bid is the last Bid quote
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// the last_price_ask is the last Ask quote
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

Die ganzzahlige file_handle  Variable dient zur Speicherung des Datei-Identifikators bei den Dateiabläufen. Die BidPosition and AskPosition dient zur Speicherung der Startpositionen der Brief- und Geldkurse im String. Die the line_string_len dient zur Festlegung der Stringlänge, die von der Datei gelesen wird, und die Variable i wird als Schleifen-Zähler verwendet. Die Werte der zuletzt erhaltenen Brief- und Geldkurse werden in den Variablen last_price_bid  und last_price_ask gespeichert. Die Dateiname-String Variable dient zur Speicherung des Dateinamens, der file_buffer ist ein String, der zum Lesen und Schreiben der Datei verwendet wird.

Der Dateiname besteht aus der path_prefix Variable, dem Namen des Finanzinstruments und der Dateierweiterung ".txt". Die Verwendung der Funktion StringConcatenate bietet sich hierbei stärker an als eine Stringverknüpfung mittels des Additions-Operators, da diese Funktion scheller arbeitet und weitaus sparsamer mit dem Memory umgeht.

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

Wir öffnen nun die Datei mit Hilfe der Funktion FileOpen, um sie weiter zu verwenden:

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

Wir verwenden die FILE_READ und FILE_WRITE Markierungen, da wir die Daten lesen und in die Datei schreiben werden. Der FILE_ANSI Markierung gibt an, dass die ANSI Codeseite verwendet wird (Standardeinstellung ist Unicode). Der FILE_SHARE_READ Markierung bedeutet, dass ein gemeinsamer Zugriff zum Lesen durch andere Anwendungen während der Arbeit mit der Datei, gestattet ist.

Beim ersten Start des Indikators gibt es keine Daten (oder der Chart-Zeitraum wurde verändert):

 // At first execution of OnCalculate function, we are reading the quotes from a file
 if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
    {
      // Reading a string from the file
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
         // Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid line should be plotted, adding this value to BidBuffer[] array
         if(BidLineEnable) 
         BidBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask line should be plotted, adding this value to AskBuffer[] array
         if(AskLineEnable) 
         AskBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

Wir begrenzen die Anzahl der Quoten, die von der Datei gelesen werden sollen, auf die Hälfte der Anzahl der Bars auf dem Chart. Als Erstes lesen wir den String von der Datei und legen seine Länge fest. Am Ende der Zeile finden sich zwei zusätzliche Zeichen mit den Codes 10 und 13 ("neue Zeile" und "Zeilenumbruch"), daher müssen wir die Zeilenlänge um 2 erhöhen.

Wir gehen davon aus, dass die durchschnittliche Länge der übrigen Zeilen der Datei gleich ist. Sollte ihre Länge größer sein als das Produkt der Länge einer Zeile bei rates_total/2 (d.h. die Datei enthält mehr Quoten als rates_total/2), lesen wir nur die letzten rates_total/2 Quoten. Dazu setzen wir den Dateizeiger in dem Abstand, der dem Produkt einer Stringlänge durch rates_total/2  entspricht (vom Ende der Datei an) und lesen eine Zeile von der Datei, um den Dateizeiger an den Anfang der Zeile anpassen zu können.

Bitte beachten Sie, dass wir zwei Werte mit Hilfe des if Operators vergleichen, die unterschiedlicher Art sind: die Dateilänge ist ein ulong Typ, und der Ausdruck auf der rechten Seite ist ein int Typ. Daher führen wir eine explizite Umwandlung (TypeCasting) des rechtsseitigen Ausdrucks in den ulong Typ durch.

Enthält die Datei weniger Quoten als rates_total/2, bewegen wir den Dateizeiger zum Anfang der Datei. 

Wir stellen den Quotenzähler auf Null und lesen die Zeilen von der Datei, bis wir ihr Ende erreicht haben. Die Stringbearbeitung wird für Strings mit einer Zeichenlänge von > sechs Zeichen ausgeführt: dies ist eine Mindest-Stringlänge, die ein Zeichen für Datum, Uhrzeit, Brief und Geld sowie Trenner dazwischen besitzt. Die Brief- und Geldwerte extrahieren wir dann von einem String, der von der Datei gelesen wird, wenn die entsprechen Zeile graphisch dargestellt werden soll, und erhöhen den Quotenzähler.

Sind die Daten zuvor schon gelesen worden, bewegen wir den Dateizeiger mit Hilfe der Funktion FileSeek ans Ende der Datei (die neuen Daten werden jetzt in die Datei geschrieben). Mittels der Funktion StringConcatenate erzeugen wir den String, der dann mit der FileWrite Funktion in eine Datei geschrieben wird Wir ergänzen die BidBuffer[]  und AskBuffer[] Arrays mit den neuen Werten der Brief- und Geldkurse, wenn die entsprechende Zeile graphisch dargestellt werden soll und erhöhen den Quotenzähler.

  // If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid line should be plotted, adding the last Bid price to the BidBuffer[] array
   if(BidLineEnable) BidBuffer[ticks_stored]=last_price_bid;
   // If the Ask line should be plotted, adding the last Ask price to the AskBuffer[] array
   if(AskLineEnable) AskBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

Man kann sich hier natürlich fragen, warum man die Daten einer Datei nicht innerhalb der OnInit Funktion ausliest? Die Antwort darauf lautet: Die Länge der BidBuffer[] und AskBuffer[]  dynamischen Arrays ist nicht festgelegt worden, und wird dann angegeben, wenn die OnCalculate Funktion aufgerufen wird.

Die zuvor geöffnete Datei wird nun geschlossen:

// Closing the file
FileClose(file_handle);

Für den Fall, dass der Quotenzähler gleich oder größer der Anzahl an Bars auf dem Chart nach dem Lesen der Datei oder nach dem hinzufügen der BidBuffer[] und AskBuffer[] Arrays ist, wird die Hälfte der alten Quoten entfernt und die übrigen an ihren Platz verschoben.

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // If the Bid line should be plotted, shifting the values of BidBuffer[] array on tick_stored/2
      if(BidLineEnable) BidBuffer[i-ticks_stored/2]=BidBuffer[i];
      // If the Ask line should be plotted, shifting the values of AskBuffer[] array on tick_stored/2
      if(AskLineEnable) AskBuffer[i-ticks_stored/2]=AskBuffer[i];
     }
   // Changing the value of a counter
   ticks_stored-=ticks_stored/2;
  }

Die BidBuffer[]  und AskBuffer[] Arrays der Indikator-Buffer sind keine Zeitreihen, sodass das jüngste Element einen Index gleich ticks_stored-1 hat, und der jüngste Chart-Bar einen Index gleich rates_total-1 besitzt. Um beide auf der gleichen Ebene kombinieren zu können, müssen wir die Zeile des Indikators mit Hilfe der PlotIndexSetInteger Funktion verschieben:

// Shifting the Bid line to align with the price chart
PlotIndexSetInteger(0,PLOT_SHIFT,rates_total-ticks_stored);
// Shifting the Ask line to align with the price chart
PlotIndexSetInteger(1,PLOT_SHIFT,rates_total-ticks_stored);

Die Werte der jüngst erhaltenen Kurse werden in BidBuffer[]  und AskBuffer[]  gespeichert, mit dem Index gleich rates_total-1 (wenn die entsprechenden Zeilen graphisch dargestellt werden sollen), um sie im Indikatorfenster oben links anzuzeigen.

// If the Bid line should be plotted, placing the value to the last element 
// of BidBuffer [] array to show the last Bid price in the indicator's window  
if(BidLineEnable) BidBuffer[rates_total-1]=last_price_bid;
// If the Ask line should be plotted, placing the value to the last element 
// of AskBuffer [] array to show the last Ask price in the indicator's window
if(AskLineEnable) AskBuffer[rates_total-1]=last_price_ask;

Die Ausführung der OnCalculate Funktion wird durch die Lieferung von rates_total abgeschlossen (Sie können jede Zahl ungleich Null liefern) und der Code der Funktion endet mit einer geschweiften Klammer.

// Return from OnCalculate(), return a value, different from zero   
return(rates_total);
}

Der Kursschwankung-Indikator ist nun geschrieben. Der komplette Quellcode für den Indikator kann mittels des Links am Ende dieses Beitrags heruntergeladen werden.

Erzeugung des Indikators für "Kursschwankungs-Kerzen"

Jetzt schreiben wir einen Indikator, der sog. "Kursschwankungen in Form von Kerzen" graphisch darstellt. Anders als bei einem herkömmlichen Kerzenchart, wo jede Kerze einen spezifischen Zeitraum abbildet, hat das Chart der "Kursschwankungs-Kerzen" eine andere Struktur: jede Kerze hat eine vorab festgelegte Anzahl an Kursschwankungen, die vom Makler erhalten wurde (Equivolume Kerzen-Chart). Abb. 2 zeigt, wie dieser Indikator aussieht:


Abb. 2 Indikator für "Kursschwankungs-Kerzen"

Indikator für "Kursschwankungs-Kerzen", wie auch der oben erklärte Indikator für Kursschwankungen, schreibt alle eingehenden Quoten auf die Datei. Datenformat und Datenstandort sind gleich. Dateipfad, Namens-Präfix, Anzahl der Kursschwankungen pro Kerze und Kurstyp (Brief- oder Geldkurs) können in den Indikator-Optionen festgelegt werden.

Zur Erzeugung eines Indikators, startet man den MetaTrader5 Client-Terminal und den MetaQuotes Language Editor durch Drücken von "F4".

Wir legen fest, dass der Indikator in einem separaten Fenster gezeichnet werden soll:

// Indicator is plotted in a separate window
#property indicator_separate_window

Er verfügt über nur ein graphisches Element: farbige Kerzen.

// One graphic plot is used, color candles
#property indicator_plots 1

Zur Anzeige der farbigen Kerzen und der Speicherung der Kursdaten-Werte (Eröffnung, hoch, niedrig und Schluss) jedes Kurses für jede Kerze brauchen wir vier Buffer. Zudem brauchen wir einen zusätzlichen Buffer zur Speicherung der Farb-Indices der Kerzen.

// We need 4 buffers for OHLC prices and one - for the index of color
#property indicator_buffers 5

Legen wir zunächst die Zeichnungsart fest: DRAW_COLOR_CANDLES - farbige Kerzen

// Specifying the drawing type - color candles
#property indicator_type1 DRAW_COLOR_CANDLES

Dann die Farben für die Kerzen:

// Specifying the colors for the candles
#property indicator_color1 Gray,Red,Green

Als Nächstes price_types  des Aufzählungstyps, der eine der folgenden Werte enthält: Brief oder Geld:

/ / Declaration of the enumeration
enum price_types
  (
   Bid,
   Ask
  )

Nun legen wie die Eingabe-Parameter fest, die vom Benutzer im Optionen-Menü des Indikators verändert werden können:

// The ticks_in_candle input variable specifies the number of ticks,
// corresponding to one candle
input int ticks_in_candle=16; //Tick Count in Candles
// The applied_price input variable of price_types type indicates 
// the type of the data, that is used in the indicator: Bid or Ask prices.
input price_types applied_price=0; // Price
// The path_prefix input variable specifies the path and prefix to the file name
input string path_prefix=""; // FileName Prefix

Die Variable ticks_in_candle legt die Anzahl der Kursschwankungen fest, die einer Kerze entsprechen. Die Variable applied_price gibt den Kurstyp an, der zur Erzeugung der Kerzen verwendet wird: Brief oder Geld. Der Name des Directory und das Dateinamen-Präfix der Datei mit den Kursschwankungsdaten-Historie können in der Variable path_prefix festgelegt werden.

Die Variablen mit Werten, die zwischen den Aufrufen des Indikators gespeichert werden sollen, werden auf globaler Ebene deklarierrt.

// The ticks_stored variable contains the number of stored quotes
int ticks_stored;
// The TicksBuffer [] array is used to store the incoming prices
// The OpenBuffer [], HighBuffer [], LowBuffer [] and CloseBuffer [] arrays
// are used to store the OHLC prices of the candles
// The ColorIndexBuffer [] array is used to store the index of color candles
double TicksBuffer[],OpenBuffer[],HighBuffer[],LowBuffer[],CloseBuffer[],ColorIndexBuffer[];

Die Variable ticks_stored  wird zur Speicherung der Anzahl der verfügbaren Quoten verwendet. Das TicksBuffer[]  Array wird zur Speicherung der erhaltenen Quoten verwendet. Die OpenBuffer[], HighBuffer[], LowBuffer[] und CloseBuffer[] Arays werden zur Speicherung der Kerzen-Kurse verwendet (Eröffnung, höchster, niedrigster und Schluss), die alle auf dem Chart gezeichnet werden. Das ColorIndexBuffer[]  Array wird zur Speicherung des Farb-Index der Kerzen verwendet.

Die OnInit Funktion gibt an, dass die OpenBuffer[], HighBuffer[], LowBuffer[] und CloseBuffer[] Arrays als Indikator-Buffer verwendet werden. Das ColorIndexBuffer[]  Array enthält einen Farb-Index der Kerzen und das TicksBuffer[] Array wird für Zwischenberechnungen verwendet:

void OnInit()
  {
   // The OpenBuffer[] array is an indicator buffer
   SetIndexBuffer(0,OpenBuffer,INDICATOR_DATA);
   // The HighBuffer[] array is an indicator buffer
   SetIndexBuffer(1,HighBuffer,INDICATOR_DATA);
   // The LowBuffer[] array is an indicator buffer
   SetIndexBuffer(2,LowBuffer,INDICATOR_DATA);
   // The CloseBuffer[] array is an indicator buffer
   SetIndexBuffer(3,CloseBuffer,INDICATOR_DATA);
   // The ColorIndexBuffer[] array is the buffer of the color index
   SetIndexBuffer(4,ColorIndexBuffer,INDICATOR_COLOR_INDEX);
   // The TicksBuffer[] array is used for intermediate calculations
   SetIndexBuffer(5,TicksBuffer,INDICATOR_CALCULATIONS);

Als nächsten Schritt legen wir die OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]  und ColorIndexBuffer[] Arrays als Zeitreihen fest (d.h. die jüngsten Daten haben den Index "0"):

   // The indexation of OpenBuffer[] array as timeseries
   ArraySetAsSeries(OpenBuffer,true);
   // The indexation of HighBuffer[] array as timeseries
   ArraySetAsSeries(HighBuffer,true);
   // The indexation of LowBuffer[] array as timeseries
   ArraySetAsSeries(LowBuffer,true);
   // The indexation of CloseBuffer[] array as timeseries
   ArraySetAsSeries(CloseBuffer,true);
   // The indexation of the ColorIndexBuffer [] array as timeseries
   ArraySetAsSeries(ColorIndexBuffer,true);

Die Werte der Indikator-Buffer gleich "0", sollen also auf dem Chart nicht dargestellt werden:

   // The null values of Open prices (0th graphic plot) should not be plotted
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // The null values of High prices (1st graphic plot) should not be plotted
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
   // The null values of Low prices (2nd graphic plot) should not be plotted
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0);
   // The null values of Close prices (3rd graphic plot) should not be plotted
   PlotIndexSetDouble(3,PLOT_EMPTY_VALUE,0);

Das Schreiben der OnInit Funktion ist nun abgeschlossen und wir schließen die Funktion mit einer geschweiften Klammer ab.

Jetzt müssen wir die OnCalculate Funktion schreiben. Dazu legen wir zunächst alle an die Funktionen übertragenen Parameter fest:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

Wir geben die Variablen an, die in der OnInit Funktion verwendet werden sollen.

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, 
// CandleNumber - number of candle, for which the prices OHLC are determined,
// i - loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,CandleNumber,i;
// The last_price_bid variable is the recent received Bid price
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// The last_price_ask variable is the recent received Ask price
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

Die ganzzahlige Variable file_handle wird zur Speicherung des Datei-Identifikators bei den Dateiabläufen verwendet. BidPosition und AskPosition werden zur Speicherung der Startpositionen des Brief- und Geldkurses im String verwendet. line_string_len  ist eine Stringlänge, die von der Datei gelesen wird. CandleNumber ist der Index der berechneten Kerze, und die Variable i  wird als Schleifenzähler verwendet.

Die jüngst erhaltenen Brief- und Geldkurse werden in den last_price_bid  und last_price_ask  Doppeltyp-Variablen gespeichert. Die Variable filename  (Stringtyp) dient zur Speicherung des Dateinamens; file_buffer  ist ein String, der bei Dateiabläufen verwendet wird.

Die Größe des TicksBuffer[] Arrays wird im Gegensatz zu den OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]  und ColorIndexBuffer[]  Arrays, die Indikator-Buffer sind, nicht automatisch eingestellt, daher setzen wir die Größe eines TicksBuffer[]  Array als genauso groß wie die OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[] und ColorIndexBuffer[] Arrays fest:

// Setting the size of TicksBuffer[] array
ArrayResize(TicksBuffer,ArraySize(CloseBuffer));

Nun bereiten wir aus der Variable path_prefix den Dateinamen, den Namen des Finanzinstruments und die Erweiterung ".txt" vor:

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

Die Datei wird nun mit den Parametern, wie oben für den vorigen Indikator beschrieben, geöffnet.

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

Wird die OnCalculate Funktion das erste Mal aufgerufen wird und sich imTicksBuffer[]  Array keine Daten befinden, lesen wir sie von der Datei:

if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
     {
      // Reading a string from thefile
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
          //Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid prices are used, adding the Bid price to TicksBuffer[] array
         if(applied_price==0)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask prices are used, adding the Ask price to TicksBuffer[] array
         if(applied_price==1)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

Das Lesen der Quoten von der Datei ist bereits oben detaillierter beschrieben worden - der Vorgang ist der gleiche wie beim o.g. Indikator.

Wenn die Quoten nicht zuvor in das TicksBuffer[]  Array gelesen wurden, schreiben wir neue Kurswerte in die Datei, platzieren einen neuen Kurs in das TicksBuffer[]  Array und erhöhen den Quotenzähler:

// If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid prices are used, adding the last Bid price to TicksBuffer[] array
   if(applied_price==0) TicksBuffer[ticks_stored]=last_price_bid;
   // If the Ask prices are used, adding the last Ask price to TicksBuffer[] array
   if(applied_price==1) TicksBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

Jetzt kann die Datei geschlossen werden:

// Closing the file
FileClose(file_handle);

Sobald die Anzahl der gespeicherten Quoten die Anzahl der Bars auf dem Kurschart erreicht oder überschreitet, entfernen wir die Hälfte der ältesten Daten und verschieben die übrigen:

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // Shifting the data to the beginning in the TicksBuffer[] array on tick_stored/2
      TicksBuffer[i-ticks_stored/2]=TicksBuffer[i];
     }
   // Changing the quotes counter
   ticks_stored-=ticks_stored/2;
  }

Berechnen wir nun die OHLC-Werte für jede Kerze und platzieren diese Werte in die entsprechenden Indikator-Buffer:

   // We assign the CandleNumber with a number of invalid candle
   CandleNumber=-1;
   // Search for all the price data available for candle formation
   for(i=0;i<ticks_stored;i++)
     {
      // If this candle is forming already
      if(CandleNumber==(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle)))
        {
         // The current quote is still closing price of the current candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is greater than the highest price of the current candle,
          // it will be a new highest price of the candle
         if(TicksBuffer[i]>HighBuffer[CandleNumber]) HighBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is lower than the lowest price of the current candle, 
          // it will be a new lowest price of the candle
         if(TicksBuffer[i]<LowBuffer[CandleNumber]) LowBuffer[CandleNumber]=TicksBuffer[i];
         // If the candle is bullish, it will have a color with index 2 (green)
         if(CloseBuffer[CandleNumber]>OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=2;
         // If the candle is bearish, it will have a color with index 1 (red)
         if(CloseBuffer[CandleNumber]<OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=1;
         // If the opening and closing prices are equal, then the candle will have a color with index 0 (grey)
         if(CloseBuffer[CandleNumber]==OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=0;
        }
      // If this candle hasn't benn calculated yet
      else
        {
         // Let's determine the index of a candle
         CandleNumber=(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle));
         // The current quote will be the opening price of a candle
         OpenBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the highest price of a candle
         HighBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the lowest price of a candle
         LowBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the closing price of a candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // The candle will have a color with index 0 (gray)
         ColorIndexBuffer[CandleNumber]=0;
        }
     }

Die Ausführung der OnCalculate Funktion ist abgeschlossen, sobald ein Wert ungleich Null geliefert wird, also das TicksBuffer[] Array bereits die Daten hat und sie beim nächsten Aufruf der Funktion nicht mehr eingelesen werden müssen. Jetzt können wir die Funktion mit einer geschweiften Klammer am Ende abschließen.

 // Return from OnCalculate(), return a value, different from zero   
 return(rates_total);
}

Am Ende dieses Beitrags findet sich ein Link mit dem der komplette Quellcode des Indikators heruntergeladen werden kann.

Fazit

In diesem Beitrag wurde die Erzeugung von zwei Kursschwankungs-Indikatoren behandelt: der Kursschwankungs-Chart Indikator und der "Kursschwankungs-Kerzen" Indikator.

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

Beigefügte Dateien |
tickindicator.mq5 (8.28 KB)
Neue Möglichkeiten mit MetaTrader5 Neue Möglichkeiten mit MetaTrader5
MetaTrader 4 erfreute sich bei Händlern auf der ganzen Welt großer Beliebtheit und es sah lange so aus, als wären alle nun wunschlos glücklich. Mit seiner hohen Arbeitsgeschwindigkeit, seiner robusten Zuverlässigkeit, einem Riesenfeld an Möglichkeiten zum Schreiben von Indikatoren, Expert Advisors und informierten Handelssystemen sowie seiner Fähigkeit, aus über 100 Maklern auswählen zu können, hat sich dieses Terminal deutlich vom Rest abgesetzt. Doch die Zeit steht nicht still und deshalb stehen wir jetzt vor der Wahl: MetaTrade 4 oder MetaTrade 5? In diesem Beitrag sollen die wichtigsten Unterschiede dieses Terminals der 5. Generation aus aktuellem Blickwinkel beschrieben werden.
Unterschiedliche Zeichnungsstile in MQL5 Unterschiedliche Zeichnungsstile in MQL5
In MQL4 gibt es 6 - in MQL5 18 Zeichnungsstile. Aus diesem Grund ist ein Beitrag zur Präsentation der Zeichnungsstile von MQL5 durchaus angebracht. Im Folgenden werden daher die Zeichnungsstile in MQL5 im Einzelnen betrachtet. Darüber hinaus erzeugen wir einen Indikator zur Demonstration, wie man diese Zeichnungsstile nutzt und die graphische Darstellung (Plot) verfeinert.
Schrittweiser Leitfaden für Anfänger zum Schreiben eines Expert Advisors in MQL5 Schrittweiser Leitfaden für Anfänger zum Schreiben eines Expert Advisors in MQL5
Die Programmierung des Expert Advisors in MQL5 ist einfach und kann problemlos erlernt werden. In diesem Leitfaden werden nacheinander die zum Schreiben eines einfachen Expert Advisors auf Grundlage einer entwickelten Handels-Strategie erforderlichen, grundlegenden Schritte erklärt. Es werden hier die Struktur eines Expert Advisors, die Verwendung eingebauter technischer Indikatoren und Handels-Funktionen, die Details des Fehlersuch(Debug)-Modus und die Verwendung des Strategie-Testers präsentiert.
Wie man in MQL5 Indikatoren aufruft Wie man in MQL5 Indikatoren aufruft
Mit der neuen, zur Verfügung stehenden Version der MQL Programmiersprache hat sich nicht nur der Ansatz im Umgang mit Indikatoren verändert, sondern es haben sich auch neue Möglichkeiten ihrer Erzeugung ergeben. Zudem hat man jetzt noch mehr Flexibilität bei der Arbeit mit den Indikator-Buffern - man kann jetzt die gewünschte Richtung der Indizierung angeben und genau so viele Indikatorwerte bekommen, wie man möchte. In diesem Beitrag werden die einfachen Methoden zum Aufruf von Indikatoren und Abruf von Daten aus dem Indikator-Buffer erklärt.