Besonderheiten von Kundenindikatoren
Das Schreiben von Kundenindikatoren im Handelssystem MetaTrader hat auch einige Besonderheiten.
Damit man ein Programm als Kundenindikator bezeichnen könnte, muss es einer der zwei Definitionen entsprechen:
#property indicator_chart_window // der Indikator wird im Hauptfenster des Charts gezeichnet
oder
#property indicator_separate_window // der Indikator wird in einem separaten Fenster gezeichnet
Um dem Maßstab des separaten Fensters zu fixieren, werden folgende Definitionen genutzt:
#property indicator_minimum Min_Value #property indicator_maximum Max_Value
dabei sind "Min_Value" und "Max_Value" entsprechende Zahlen. z.B.: für den Kundenindikator RSI müssen diese Werte 0 und 100 betragen.
Die Anzahl der Indikatorarrays, die notwendig sind, um einen Indikator zu zeichnen, wird mit der folgenden Definition eingestellt:
#property indicator_buffer N
wo der N-Wert von 1 bis 8 liegen kann.
Die Farbe der Indikatorlinie wird mit der folgenden Definition eingestellt:
#property indicator_color1 Blue #property indicator_color2 Red ... #property indicator_colorN <SomeColor>
wo N die Anzahl der Indikatorarrays beträgt, die mit der "#property indicator_buffer" definiert wurde.
Es gibt eine Reihe von Funktionen, anhand derer man die Berechnung und die Visualisierung des Indikators steuern kann. Als Beispiel wird der Kundenindikator Ichimoku Kinko Hyo verwendet:
//+------------------------------------------------------------------+ //| Ichimoku.mq4 | //| Copyright © 2004, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2004, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" #property indicator_chart_window #property indicator_buffers 7 #property indicator_color1 Red #property indicator_color2 Blue #property indicator_color3 SandyBrown #property indicator_color4 Thistle #property indicator_color5 Lime #property indicator_color6 SandyBrown #property indicator_color7 Thistle //---- input parameters extern int Tenkan=9; extern int Kijun=26; extern int Senkou=52; //---- indicator buffers double Tenkan_Buffer[]; double Kijun_Buffer[]; double SpanA_Buffer[]; double SpanB_Buffer[]; double Chinkou_Buffer[]; double SpanA2_Buffer[]; double SpanB2_Buffer[]; //---- span_a drawing begin int a_begin; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,Tenkan_Buffer); SetIndexDrawBegin(0,Tenkan-1); SetIndexLabel(0,"Tenkan Sen"); //---- SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,Kijun_Buffer); SetIndexDrawBegin(1,Kijun-1); SetIndexLabel(1,"Kijun Sen"); //---- a_begin=Kijun; if(a_begin<Tenkan) a_begin=Tenkan; SetIndexStyle(2,DRAW_HISTOGRAM,STYLE_DOT); SetIndexBuffer(2,SpanA_Buffer); SetIndexDrawBegin(2,Kijun+a_begin-1); SetIndexShift(2,Kijun); SetIndexLabel(2,NULL); SetIndexStyle(5,DRAW_LINE,STYLE_DOT); SetIndexBuffer(5,SpanA2_Buffer); SetIndexDrawBegin(5,Kijun+a_begin-1); SetIndexShift(5,Kijun); SetIndexLabel(5,"Senkou Span A"); //---- SetIndexStyle(3,DRAW_HISTOGRAM,STYLE_DOT); SetIndexBuffer(3,SpanB_Buffer); SetIndexDrawBegin(3,Kijun+Senkou-1); SetIndexShift(3,Kijun); SetIndexLabel(3,NULL); SetIndexStyle(6,DRAW_LINE,STYLE_DOT); SetIndexBuffer(6,SpanB2_Buffer); SetIndexDrawBegin(6,Kijun+Senkou-1); SetIndexShift(6,Kijun); SetIndexLabel(6,"Senkou Span B"); //---- SetIndexStyle(4,DRAW_LINE); SetIndexBuffer(4,Chinkou_Buffer); SetIndexShift(4,-Kijun); SetIndexLabel(4,"Chinkou Span"); //---- return(0); } //+------------------------------------------------------------------+ //| Ichimoku Kinko Hyo | //+------------------------------------------------------------------+ int start() { int i,k; int counted_bars=IndicatorCounted(); double high,low,price; //---- if(Bars<=Tenkan || Bars<=Kijun || Bars<=Senkou) return(0); //---- initial zero if(counted_bars<1) { for(i=1;i<=Tenkan;i++) Tenkan_Buffer[Bars-i]=0; for(i=1;i<=Kijun;i++) Kijun_Buffer[Bars-i]=0; for(i=1;i<=a_begin;i++) { SpanA_Buffer[Bars-i]=0; SpanA2_Buffer[Bars-i]=0; } for(i=1;i<=Senkou;i++) { SpanB_Buffer[Bars-i]=0; SpanB2_Buffer[Bars-i]=0; } } //---- Tenkan Sen i=Bars-Tenkan; if(counted_bars>Tenkan) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Tenkan; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } Tenkan_Buffer[i]=(high+low)/2; i--; } //---- Kijun Sen i=Bars-Kijun; if(counted_bars>Kijun) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Kijun; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } Kijun_Buffer[i]=(high+low)/2; i--; } //---- Senkou Span A i=Bars-a_begin+1; if(counted_bars>a_begin-1) i=Bars-counted_bars-1; while(i>=0) { price=(Kijun_Buffer[i]+Tenkan_Buffer[i])/2; SpanA_Buffer[i]=price; SpanA2_Buffer[i]=price; i--; } //---- Senkou Span B i=Bars-Senkou; if(counted_bars>Senkou) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Senkou; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } price=(high+low)/2; SpanB_Buffer[i]=price; SpanB2_Buffer[i]=price; i--; } //---- Chinkou Span i=Bars-1; if(counted_bars>1) i=Bars-counted_bars-1; while(i>=0) { Chinkou_Buffer[i]=Close[i]; i--; } //---- return(0); } //+------------------------------------------------------------------+
Die Funktion "SetIndexStyle" steuert die Zeichnungsparameter eines Indikatorarrays. Im Zeichnungsmodus DRAW_LINE werden Linien zwischen den Werten gezeichnet, die im entsprechenden Indikatorarray definiert wurden. Der Modus DRAW_HISTOGRAM, angewandt zum Indikator des Hauptfensters, hat seine eigenen Besonderheiten. Das Histogramm wird zwischen den entsprechenden Werten der zwei Indexarrays gezeichnet: einem geraden (in unserem Fall - SpanA_Buffer) und einem ungeraden (SpanB_Buffer). Dabei wird die Farbe desjenigen Indexarrays verwendet, dessen Wert höher ist.
Die Funktion "SetIndexDrawBegin" zeigt, von welchem Element die wichtigen Daten des Indikatorarrays beginnen.
Die Funktion "SetIndexBuffer" erklärt jedes eindimensionale "double" Array zum Indexarray. Dabei werden die Indexarrays durch das System gesteuert. Gerade aus diesem Grund braucht man die Größe dieser Arrays nicht anzugeben.
//---- indicator buffers double Tenkan_Buffer[]; double Kijun_Buffer[]; double SpanA_Buffer[]; double SpanB_Buffer[]; double Chinkou_Buffer[]; double SpanA2_Buffer[]; double SpanB2_Buffer[];
Zu den Indikatorarrays darf man nicht die Funktion ArrayResize anwenden - das macht keinen Sinn. Unnötig ist auch die Anwendung der Funktion ArrayInitialize zu den Indikatorarrays, insbesondere als "init", wenn die Inidkatorarrays noch nicht verteilt sind. Die Indikatorarrays werden bei der Zu-und Neuordnung des Speichers automatisch initialisiert. Als Initialisierungswert wird EMPTY_VALUE oder der durch die Funktion SetIndexEmptyValue angegebener Wert verwendet. Die "leeren" Werte werden nicht angezeigt.
Die Funktion "SetIndexLabel" setzt den Namen, der in Tool-Tips und im Datenfenster neben dem entsprechenden Wert angezeigt wird (voreingestellt ist der Name "ValueN", wo N die Nummer des Indexarrays ist). Wenn statt des Namens Null anzugeben, wird der entsprechende Wert weder in den Tool-Tips noch im Datenfenster angezeigt. Im gegebenen Fall werden Wolken mit einem Histogramm schraffiert und mit einer Linie begrenzt. Dabei sind die Werte der "Linie-" und der "Histogrammarrays" gleich und man kann nur einen der beiden anzeigen.
Die Funktion "IndicatorCounted" lässt den Indikator sparsam berechnen. Diese Funktion liefert die Anzahl der Balken, die zum Zeitpunkt des vorherigen Starts des Indikator vorhanden sind, d.h. die Anzahl der bereits kalkulierten Balken (wenn dabei kein Fehler aufgetreten ist und das Programm nicht vorzeitig beendet wurde), die nicht mehr umgerechnet werden müssen. Wenn der Kundenindikator reinitialisiert wird oder wenn historische Daten weitgehend aktualisiert werden, wird diese Anzahl automatisch auf Null gestellt.
Betrachten wir noch ein Beispiel. Kundenindikator - Accelerator/Decelerator Oscillator:
//+------------------------------------------------------------------+ //| Accelerator.mq4 | //| Copyright © 2005, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2005, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- indicator settings #property indicator_separate_window #property indicator_buffers 3 #property indicator_color1 Black #property indicator_color2 Green #property indicator_color3 Red //---- indicator buffers double ExtBuffer0[]; double ExtBuffer1[]; double ExtBuffer2[]; double ExtBuffer3[]; double ExtBuffer4[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- 2 additional buffers are used for counting. IndicatorBuffers(5); //---- drawing settings SetIndexStyle(0,DRAW_NONE); SetIndexStyle(1,DRAW_HISTOGRAM); SetIndexStyle(2,DRAW_HISTOGRAM); IndicatorDigits(Digits+2); SetIndexDrawBegin(0,38); SetIndexDrawBegin(1,38); SetIndexDrawBegin(2,38); //---- 4 indicator buffers mapping SetIndexBuffer(0,ExtBuffer0); SetIndexBuffer(1,ExtBuffer1); SetIndexBuffer(2,ExtBuffer2); SetIndexBuffer(3,ExtBuffer3); SetIndexBuffer(4,ExtBuffer4); //---- name for DataWindow and indicator subwindow label IndicatorShortName("AC"); SetIndexLabel(1,NULL); SetIndexLabel(2,NULL); //---- initialization done return(0); } //+------------------------------------------------------------------+ //| Accelerator/Decelerator Oscillator | //+------------------------------------------------------------------+ int start() { int limit; int counted_bars=IndicatorCounted(); double prev,current; //---- last counted bar will be recounted if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; //---- macd counted in the 1-st additional buffer for(int i=0; i<limit; i++) ExtBuffer3[i]=iMA(NULL,0,5,0,MODE_SMA,PRICE_MEDIAN,i)- iMA(NULL,0,34,0,MODE_SMA,PRICE_MEDIAN,i); //---- signal line counted in the 2-nd additional buffer for(i=0; i<limit; i++) ExtBuffer4[i]=iMAOnArray(ExtBuffer3,Bars,5,0,MODE_SMA,i); //---- dispatch values between 2 buffers bool up=true; for(i=limit-1; i>=0; i--) { current=ExtBuffer3[i]-ExtBuffer4[i]; prev=ExtBuffer3[i+1]-ExtBuffer4[i+1]; if(current>prev) up=true; if(current<prev) up=false; if(!up) { ExtBuffer2[i]=current; ExtBuffer1[i]=0.0; } else { ExtBuffer1[i]=current; ExtBuffer2[i]=0.0; } ExtBuffer0[i]=current; } //---- done return(0); } //+------------------------------------------------------------------+
Die Funktion "IndicatorBuffers" setzt die Anzahl der Puffer, anhand derer der Indikator berechnet wird. In der Regel wird diese Funktion aufgerufen, wenn mehr Indexarrays genutzt werden als es nötig ist, um einen Indikator zu zeichnen. Die zusätzlichen Arrays werden durch das System gesteuert.
Die Funktion "SetIndexDigits" steuert die Genauigkeit der Informationsausgabe. In diesem Fall ist die übliche Genauigkeit von vier Zeichen nach dem Komma nicht ausreichend, um den Unterschied zwischen zwei gleitenden Durchschnitten und den weiteren Unterschied zwischen dem Ergebnis und einer Signallinie zu berechnen.
Die Funktion "SetIndexDrawBegin" zeigt, von welchem Element die wichtigen Daten des Indikatorarrays beginnen. Im gegebenen Fall wird die Signallinie als einfacher gleitender Durchschnitt vom anderen einfachen gleitenden Durchschnitt berechnet. Aus diesem Grund sind die ersten 38 Werte des Indikators leer und werden nicht gezeichnet.
Die Funktion "IndicatorShortName" setzt den so genannten Kurznamen des Indikators, der in der oberen linken Ecke des Indikatorfensters sowie im Fenster "DataWindow" angezeigt wird. Wenn nichts anderes angegeben wurde, wird der Name des Kundenindikators als Kurzname benutzt. Im gegebenen Beispiel braucht man die Funktion SetIndexLabel nicht, weil nur ein Wert ausgegeben wird. Für die Ausgabe eines einzigen Wertes ist der Name des Indikators ausreichend.
Die Funktion "SetIndexStyle" steuert die Zeichnungsparameter eines Indikatorarrays. Der Modus DRAW_NONE bedeutet, dass diese Linie nicht gezeichnet werden muss. Es geht darum, dass das Histogramm im vorgestellten Indikator in zwei Farben gezeichnet werden muss. Die Daten aus dem ExtBuffer0 werden in zwei anderen Arrays verteilt - ExtBuffer1 und ExtBuffer2. Die Funktion SetIndexLabel mit dem Parameter NULL wird genutzt, um doppelte Daten in den Tool-Tips und im Datenfenster nicht auszugeben. Der Zeichnungsmodus DRAW_HISTOGRAM, angewandt zum Indikator eines separaten Fensters, lässt das Histogramm zwischen dem Nullwert und dem Wert des entsprechenden Arrays zeichnen (vgl. wie das Histogramm im Hauptfenster gezeichnet wird, siehe oben).
Die Eingangsparameter, die Indikatoren und Funktionen für Berechnungen verwenden, müssen als "extern" definiert werden und können jeglichen Typs sein.
Wenn die Eingangsparameter nicht angegeben sind, wird der entsprechende Kundenindikator am einfachsten aufgerufen.
double current_AC = iCustom( NULL, 0, "Accelerator", 0, 0 );
Die Übergabe der ersten Werte "NULL" und "0" bedeutet, dass der laufende Chart benutzt wird. Als Name des Kundenindikators wird der entsprechende Dateiname ohne Dateierweiterung mq4 genutzt. Wenn der vorletzte Parameter Null beträgt, bedeutet dies, dass die Daten aus dem allerersten Indikatorarray benötigt werden. Wenn der letzte Parameter Null beträgt, bedeutet dies, dass der Wert des allerletzten Elements des aufgerufenen Indikatorarrays benötigt wird (d.h. der neuste und aktuellste Wert).
Die Parameter werden in die Berechnungsfunktion des Kundenindikators in der Reihenfolge übergeben, in der sie beschrieben sind. Der Aufruf des Kundenindikators "Ichimoku" mit den Parametern (9,26,52) wird z.B. folgendermaßen aussehen:
iCustom( NULL, 0, "Ichimoku", 9, 26, 52, 0, shift );
Im Grunde genommen kann man die Parameter des Indikators nicht in die Funktion übergeben. Wenn im Programm keine einzige externe Variable definiert wurde, dann ist es sinnlos, Parameter zu übergeben. Man kann auch die Ausgangswerte verwenden, die bei der Beschreibung der Parameter genutzt werden. Der Aufruf desselben Kundenindikators ohne Parameter wird z.B. folgendermaßen aussehen:
iCustom( NULL, 0, "Ichimoku", 0, shift );
D.h. es werden diejenigen Werte verwendet, die die Variablen "Tenkan", "Kijun" und "Senkou" initialisieren nämlich 9, 26 und 52. Wenn aber in einem Expert Advisor ein Kundenindikator mit unterschiedlichen Parametersätzen aufgerufen werden, ist die Nutzung von Standardeinstellungen überhaupt nicht zu empfehlen.
Es ist zu bemerken, dass eine überflüssige Anzahl von Kundenindikatoren sowie inkorrekt geschriebene Indikatoren die Arbeit des Kundenterminals wesentlich verlangsamen können.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1497
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.