MTF-Indikatoren als Werkzeuge der technischen Analyse
Einführung
Die meisten Händler sind sich einig, dass eine Zustandsanalyse des aktuellen Marktes mit der Bewertung höherer Zeitrahmen beginnt. Die Analyse wird nach unten auf niedrigere Zeitrahmen bis zu demjenigen durchgeführt, in dem der Handel durchgeführt wird. Diese Analysemethode scheint ein obligatorischer Bestandteil des professionellen Ansatzes für einen erfolgreichen Handel zu sein. Um eine MTF-Analyse durchzuführen, müssen Nutzer oft mehrere Fenster öffnen oder zwischen den Charts wechseln, wenn derselbe Satz von Instrumenten verwendet wird. Somit wird die Voranalyse durchgeführt.
Was ist als Nächstes zu tun? Muss man ältere Zeiträume ignorieren oder immer wieder zwischen Fenstern und Zeitrahmen wechseln? Wenn der Arbeitszeitrahmen Н1 oder höher ist, hat der Benutzer Zeit für eine sorgfältige Bewertung der allgemeinen Situation. Bei der Arbeit an den Zeitfenstern M1-M15 bleibt jedoch keine Zeit für die Überprüfung der erforderlichen Informationen aus höheren Zeitrahmen. Diese Informationen können manchmal sehr wichtig sein. Außerdem sollten die Daten hier und jetzt verfügbar sein, nicht auf einer anderen Registerkarte oder irgendwo anders. Dies ist besonders wichtig für MTF-Strategien, die auf der gleichzeitigen Auswertung verschiedener Zeitrahmen (TF) basieren, wie z.B. die Wolfe-Wellen oder das System Elders Drei Bildschirme.
So befinden sich Händler, die diese Strategien nutzen, oft im Zustand der psychischen Belastung. Wenn man auf einem niedrigeren TFs handelt, kann man profitable Signale der höheren verpassen. Dies kann zu unsauberen Entscheidungen, vorzeitiger Schließung von Positionen oder dem Verpassen von Marktumkehrpunkten führen. Die Folgen können sehr beunruhigend sein. In diesen Situationen kann sich der Benutzer nur auf seine Erfahrung oder seine Zugriffsgeschwindigkeit auf Informationen verlassen.
Die Lösung dieses Problems ist ein Indikator, der Informationen aus verschiedenen Zeiträumen oder mehreren Handelssymbolen erhält und die umfangreichen Daten auf dem Bildschirm anzeigt, so dass der Anwender den Marktzustand effizient beurteilen kann. Neben den Basisinformationen können diese Indikatoren den tatsächlichen Zustand der Marktentwicklung darstellen und weitere Handelsaktivitäten empfehlen.
Merkmale des Algorithmus
Der wesentliche Unterschied zu den klassischen Indikatoren besteht darin, dass MTF-Indikatoren verallgemeinerte Informationen aus allen Zeitintervallen oder Finanzinstrumenten verarbeiten und dann Daten an das aktuelle Chart übergeben können. Jeder Indikatortyp (Oszillator, Trend, Lautstärke, etc.) oder eine Kombination davon kann mit mehrere Zeitrahmen arbeiten. Sie werden nach ihrem Grundalgorithmus berechnet und geben Informationen gemäß dem in den Einstellungen festgelegten Zeitintervall weiter.
Für MTF-Versionen im Vergleich zu klassischen Versionen ist keine spezielle Konfiguration erforderlich, mit Ausnahme des zusätzlichen Parameters (oder der Gruppe von Parametern) zur Angabe von Zeitintervallen oder der Liste von Symbolen zur Verwendung von Daten. Das Ergebnis kann im Hauptdiagrammfenster oder separat ausgegeben und als Signal angezeigt werden, während Gruppen je nach Gerätetyp kombiniert werden.
Klassifizierung von Indikatoren mit mehreren Zeitrahmen
Diese Indikatoren werden in allen Standardklassen dargestellt, während die meisten von ihnen vom komplexen Typ sind, d.h. Berechnungen und grafische Elemente kombinieren. Die Indikatorengruppen sind nachfolgend dargestellt:
1. Informativ: Solche Indikatoren zeigen Daten und Zusatzinformationen ohne Signale oder Grafiken an. Ein klassisches Beispiel ist der Indikator MultiTimeFrame. Es zeigt die Schließzeit der Kerze für jeden Zeitrahmen, Ask und Bid für die ausgewählten Währungspaare, den Status der Kerze (Auf-, Abwärts, DOJI) und das Volumen. Der Chart wird vom Indikator mit einer großen Menge nützlicher Informationen gefüllt, die jedoch im Handel schwer zu verwenden sind und nur angesehen werden können.
Abb. 1. Informativer Indikator
Diese Gruppe verfügt auch über unabhängige Hilfen, mit denen Handelsentscheidungen getroffen werden können. Sie zeigen die Analyseergebnisse mehrerer Standardindikatoren an, ohne dass solche Indikatoren in einem Chart installiert werden müssen (die Berechnung erfolgt automatisch), und generieren Handelsempfehlungen.
Abb. 2. Signale eines informativen Indikators
Abb. 3. Signale eines informativen Indikators
2. Grafische Indikatoren zeigen Grafiken desselben Indikators auf verschiedenen TFs. Hier ist es der standardmäßige Envelope MA(13) aus drei verschiedenen Zeitrahmen.
Abb. 4. Grafische Indikatoren
Eine weitere Art der grafischen Konstruktion ist die Gruppe von Charts mit unterschiedlichem Berechnungszeitraum. Dieser Ansatz basiert auf reiner Mathematik. D.h. der stochastische Indikator (5.3.3) hat auf M5 die Parameter von (15.3.9) aus M15 und (30.3.18) aus M30.
Abb. 5. Grafische Indikatoren mit unterschiedlichen Berechnungszeiträumen
Die obige Version kann unter Vorbehalt auf eine MTF-Klasse verweisen. Es ist nicht immer möglich, diesen Ansatz umzusetzen, während die Nachteile dieser Lösung so groß sein können, dass ihre Anwendung unangemessen wäre. Wir werden auf Fälle eingehen, in denen das Verfahren anwendbar ist, sowie danach auf die Vor- und Nachteile.
Eine separate Gruppe besteht aus den so genannten Signalindikatoren. Um zu viele grafische Elemente auf dem Arbeitsbereich zu vermeiden, bildet der Indikator Signallinien oder Grafikblöcke, die die Trendrichtung oder andere Parameter widerspiegeln. Siehe den standardmäßigen Indikator MTF_Coral mit der oben genannten MTF-Lösung:
Abb. 6. Signalindikatoren
Eine andere Gruppe kann "Fenster in einem Fenster" genannt werden. Charts mit unterschiedlichen Zeitrahmen oder Indikatoren werden im Hauptchartfenster angezeigt.
Abb. 7. "Fenster im Fenster" Indikatoren
Abb. 7.1. Ein "Fenster im Fenster" Indikatoren
Einweiteres Beispiel ist All_Woodies CCI.
Abb. 7.1. "Fenster im Fenster" Indikator All_Woodies CCI
Eine separate Gruppe beinhaltet MTF-Volatilitätsindikatoren. Dazu gehören auch MTF-Kerzen.
Abb. 8. Volatilitätsindikator MTF Kerzen
Implementierungsmethoden
Wir haben die wichtigsten Arten von MTF-Indikatoren betrachtet. Betrachten wir nun einfache Beispiele, die die wichtigsten Methoden für die lineare Implementierung zeigen. Wir werden auch die spezifischen Merkmale jeder Lösung analysieren.
Indikatoren mit mehreren Periodenlängen.
Lassen Sie uns den MA-Indikator betrachten und versuchen, die folgende Aufgabe zu lösen: Erstellen Sie eine Indikatorversion mit einem sich ändernden Berechnungszeitraum, um drei verschiedene TFs anzuzeigen.
Lassen Sie uns die wichtigsten Parameter und unsere Variablen festlegen:
//---- Indikatoreinstellungen #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_type3 DRAW_LINE #property indicator_color1 Blue #property indicator_color2 Red #property indicator_color3 Lime #property indicator_width1 1 #property indicator_width2 1 #property indicator_width3 1 //---- Eingabeparameter input ENUM_TIMEFRAMES tf1 = 1; // Zeitrahmen (1) input ENUM_TIMEFRAMES tf2 = 5; // Zeitrahmen (2)Z input ENUM_TIMEFRAMES tf3 = 15; // Zeitrahmen (3) input int maPeriod = 13; // MA Periodenlänge input int Shift = 0; // Versatz input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Glättungsverfahren input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Berechnungspreis //---- Indikatorpuffer double ExtBuf1[]; double ExtBuf2[]; double ExtBuf3[]; //---- Handles der gleitenden Durchschnitte int ExtHandle1; int ExtHandle2; int ExtHandle3; //--- Minimum der Bars für die Berechnung int ExtBarsMinimum; //--- int period1=0; int period2=0; int period3=0;
Implementieren Sie nun Array-Daten mit der Bedingung, dass der TF, an den sie angehängt sind, <= ist, als die in Variablen angegebenen.
void OnInit() { int timeframe; //---- zum Zeichnen der Indikatorpuffer SetIndexBuffer(0,ExtBuf1,INDICATOR_DATA); SetIndexBuffer(1,ExtBuf2,INDICATOR_DATA); SetIndexBuffer(2,ExtBuf3,INDICATOR_DATA); //--- timeframe =_Period; //--- if(tf1>=timeframe) { period1=maPeriod*(int)MathFloor(tf1/timeframe); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period1-1); //Setzen der ersten Bar für das Zeichnen des Index PlotIndexSetInteger(0,PLOT_SHIFT,Shift); //Versatz der Linie beim Zeichnen PlotIndexSetString(0,PLOT_LABEL,"MA("+string(period1)+")"); //Name im DatenFenster ExtHandle1=iMA(NULL,0,period1,0,InpMAMethod,InpAppliedPrice); //abrufen des Handles des MAs } //---- if(tf2>=timeframe) { period2=maPeriod*(int)MathFloor(tf2/timeframe); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,period2-1); //Setzen der ersten Bar für das Zeichnen des Index PlotIndexSetInteger(1,PLOT_SHIFT,Shift); //Versatz der Linie beim Zeichnen PlotIndexSetString(1,PLOT_LABEL,"MA("+string(period2)+")"); //Name im DatenFenster ExtHandle2=iMA(NULL,0,period2,0,InpMAMethod,InpAppliedPrice); //abrufen des Handles des MAs } //---- if(tf3>=timeframe) { period3=maPeriod*(int)MathFloor(tf3/timeframe); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,period3-1); //Setzen der ersten Bar für das Zeichnen des Indikators PlotIndexSetInteger(2,PLOT_SHIFT,Shift); //Versatz der Linie beim Zeichnen PlotIndexSetString(2,PLOT_LABEL,"MA("+string(period3)+")"); //Name im DatenFenster ExtHandle3=iMA(NULL,0,period3,0,InpMAMethod,InpAppliedPrice); //abrufen des Handles des MAs } //--- Einstellen der Genauigkeit IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- Minimum der Bars für die Berechnung int per=MathMax(period3,MathMax(period1,period2)); ExtBarsMinimum=per+Shift; //--- Initialisierung beendet }
Die Hauptschleife zur Initialisierung und Berechnung:
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[]) { //--- Prüfen von rates_total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation //--- nicht alle Daten können berechnet werden int calculated=BarsCalculated(ExtHandle1); if(calculated<rates_total&&period1!=0) { Print("Not all data of ExtHandle1 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle2); if(calculated<rates_total&&period2!=0) { Print("Not all data of ExtHandle2 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle3); if(calculated<rates_total&&period3!=0) { Print("Not all data of ExtHandle3 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } //--- wir können nicht alle Daten kopieren int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated; if(prev_calculated>0) to_copy++; } //---- Abrufen der MA-Puffer if(IsStopped()) return(0); //Prüfen des Stop-Flags if(period1!=0) if(CopyBuffer(ExtHandle1,0,0,to_copy,ExtBuf1)<=0) { Print("getting ExtHandle1 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Prüfen des Stop-Flags if(period2!=0) if(CopyBuffer(ExtHandle2,0,0,to_copy,ExtBuf2)<=0) { Print("getting ExtHandle2 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Prüfen des Stop-Flags if(period3!=0) if(CopyBuffer(ExtHandle3,0,0,to_copy,ExtBuf3)<=0) { Print("getting ExtHandle3 is failed! Error",GetLastError()); return(0); } //--- Rückgabe des Wertes von prev_calculated für den nächsten Aufruf return(rates_total); } //+------------------------------------------------------------------+
Schauen wir uns das Ergebnis an.
Abb. 9. Implementierung eines MTF-Indikators
Wir haben das Beispiel der Verwendung desselben Indikators in verschiedenen Zeiträumen unter Verwendung der Erhöhung des Berechnungszeitraums betrachtet. Mit einer leichten Änderung können wir dies nutzen und gleichzeitig die Möglichkeit bieten, individuelle Periodenlängen für jede Linie festzulegen. Diese Methode mag ineffizient erscheinen. Es ist sehr einfach, die Periodenlänge zu berechnen und mehrere Indikatoren gleichzeitig anzuwenden. In einigen Fällen ist diese Methode jedoch trotz aller Nachteile die optimale. Einer davon ist die Notwendigkeit, zwei (drei) nicht normierte Oszillatoren in einem Fenster gleichzeitig zu beobachten. Aufgrund des Amplitudenbereichs werden diese Oszillatoren gegenüber der Mittellinie verschoben und erschweren so ihre Interpretation. Diese Methode eliminiert den oben genannten Nachteil.
Indikatoren für mehrere Zeitrahmen
Statt der gut bekannten MQL4-Funktionen iClose(), iHigh(), iLow(), iOpen(), iTime(), iVolume() bietet MQL5 die Funktionen CopyTime(), CopyClose(), CopyHigh(), CopyLow(), CopyOpen(), CopyTime(), CopyVolume(), während die Funktionen iCustom, iMA, iCCI, iMACD und andere durch die Funktion CopyBuffer() implementiert sind. Jede von ihnen hat ihre Vor- und Nachteile. In unserem Fall werden wir nur MQL5 berücksichtigen. Für unseren Indikator benötigen wir die gesamte Liste der Zeitrahmen von M1 bis MN1, d.h. 26 verschiedene. Wenn Sie mehrere Handelssymbole oder -instrumente verwenden müssen, steigt diese Zahl um ein Vielfaches. In den meisten Fällen ist es nicht notwendig, die gesamte Historie zu kopieren. Bei den meisten Informationsindikatoren ist die Anzahl der Balken durch zwei begrenzt. Um zu verhindern, dass der Code zu lang wird, ist es daher besser, diese Befehle als separate Funktionen zu schreiben und wiederholt aufzurufen.
Für die Zeitreihenfunktion CopyClose() sieht die Funktion wie folgt aus:
//+------------------------------------------------------------------+ double _iClose(string symbol,int tf,int index) { if(index < 0) return(-1); double buf[]; ENUM_TIMEFRAMES timeframe=TFMigrate(tf); if(CopyClose(symbol,timeframe, index, 1, buf)>0) return(buf[0]); else return(-1); } //+------------------------------------------------------------------+
Für den Aufruf von WPR
//+------------------------------------------------------------------+ double _iWPR(string symbol, int tf, int period, int shift) { ENUM_TIMEFRAMES timeframe=TFMigrate(tf); int handle=iWPR(symbol,timeframe,period); if(handle<0) { Print("The iWPR object not created: Error ",GetLastError()); return(-1); } else return(_CopyBuffer(handle,shift)); } //+------------------------------------------------------------------+ double _CopyBuffer(int handle,int shift) { double buf[]; if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+------------------------------------------------------------------+
Gibt es mehrere Linien, wird _CopyBuffer wie folgt geschrieben:
//+------------------------------------------------------------------+ double _CopyBuffer(int handle,int index,int shift) { double buf[]; switch(index) { case 0: if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); break; case 1: if(CopyBuffer(handle,1,shift,1,buf)>0) return(buf[0]); break; case 2: if(CopyBuffer(handle,2,shift,1,buf)>0) return(buf[0]); break; case 3: if(CopyBuffer(handle,3,shift,1,buf)>0) return(buf[0]); break; case 4: if(CopyBuffer(handle,4,shift,1,buf)>0) return(buf[0]); break; default: break; } return(EMPTY_VALUE); } //+------------------------------------------------------------------+
während die Funktion _iWPR die Zeile
return(_CopyBuffer(handle,shift)
ändert auf
return(_CopyBuffer(handle,0,shift)
in beiden Fällen schaut TFMigrate() wie folgt aus:
//+------------------------------------------------------------------+ ENUM_TIMEFRAMES TFMigrate(int tf) { switch(tf) { case 0: return(PERIOD_CURRENT); case 1: return(PERIOD_M1); case 5: return(PERIOD_M5); case 15: return(PERIOD_M15); case 30: return(PERIOD_M30); case 60: return(PERIOD_H1); case 240: return(PERIOD_H4); case 1440: return(PERIOD_D1); case 10080: return(PERIOD_W1); case 43200: return(PERIOD_MN1); case 2: return(PERIOD_M2); case 3: return(PERIOD_M3); case 4: return(PERIOD_M4); case 6: return(PERIOD_M6); case 10: return(PERIOD_M10); case 12: return(PERIOD_M12); case 20: return(PERIOD_M20); case 16385: return(PERIOD_H1); case 16386: return(PERIOD_H2); case 16387: return(PERIOD_H3); case 16388: return(PERIOD_H4); case 16390: return(PERIOD_H6); case 16392: return(PERIOD_H8); case 16396: return(PERIOD_H12); case 16408: return(PERIOD_D1); case 32769: return(PERIOD_W1); case 49153: return(PERIOD_MN1); default: return(PERIOD_CURRENT); } } //+------------------------------------------------------------------+
Wie bereits erwähnt, wird für diese Berechnungsart eine begrenzte Anzahl von Elementen (Balken) benötigt. Aber manchmal ist es ratsam, die gesamte Historie zu berechnen. Man muss hier vorsichtig sein. Vergessen Sie nicht, dass es mehr historische Bars auf niedrigeren TFs als auf höheren geben wird. Diese Tatsache sollte bei der Erstellung eines solchen Tools berücksichtigt werden. Am einfachsten ist es, die kleinste Anzahl von Balken zu bestimmen und diesen Wert für die Berechnung zu verwenden. Eine schwierigere Methode ist es, diesen Wert für jedes TF separat zu bestimmen. Häufig (insbesondere bei Informationsindikatoren) werden Daten erst nach dem Schließen eines Balkens benötigt, so dass es nicht notwendig ist, ältere TFs bei jedem Tick des unteren neu zu berechnen. Wenn wir diesen Aspekt in unserer Entwicklung berücksichtigen, wird sich die Codekomplexität deutlich reduzieren.
Die Entwicklung der Informationsindikatoren (Abb. 1, Abb. 2, Abb. 3) unterscheidet sich nicht von der Entwicklung der klassischen Indikatoren, daher lassen Sie uns sofort zur Klasse der grafischen Indikatoren übergehen, die meiner Meinung nach die interessanteste ist. Informative Indikatoren benötigen nur die aktuellen Daten über den Marktzustand und die verwendeten Symbole, grafische Indikatoren haben zusätzliche Anforderungen an die Grafik. Der Balken des M5-Zeitrahmens besteht aus 5 M1 Balken, M15 aus drei M5 Balken, etc. Das heißt, wenn eine Linie auf M5 gebildet wird, wird während drei Balken eine Linie von M15 gezogen. Die Linienposition ist nicht festgelegt und kann geändert werden, bis die Kerze М15 geschlossen ist. Deshalb müssen wir die Formation an die Öffnungszeit des Kerzenständers binden. Lassen Sie uns dies am Beispiel von MA umsetzen.
//---- Indikatoreinstellungen #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_type1 DRAW_LINE #property indicator_color1 Blue #property indicator_width1 1 //---- Eingabeparameter input ENUM_TIMEFRAMES tf = 5; // Zeitrahmen input int maPeriod = 13; // MA Periodenlänge input int Shift = 0; // Versatz input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Glättungsverfahren input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Berechnungspreis input int Bars_Calculated = 500; //---- Indikatorpuffer double ExtMA[]; //---- Handles der gleitenden Durchschnitte int MA_Handle; //--- Minimum der Bars für die Berechnung int ExtBarsMinimum; ENUM_TIMEFRAMES _tf; int pf; //--- wir sichern die Anzahl der Werte im gleitenden Durchschnitt int bars_calculated=0; //+------------------------------------------------------------------+ //| Initialisierungsfunktion des nutzerdefinierten Indikators | //+------------------------------------------------------------------+ int OnInit() { _tf=tf; ENUM_TIMEFRAMES timeframe; int draw_shift=Shift;// Anfangswert PLOT_SHIFT int draw_begin=maPeriod;// Anfangswert PLOT_DRAW_BEGIN //--- timeframe=_Period; if(_tf<=timeframe)_tf=timeframe;// wenn der TF kleiner oder gleich dem aktuellen ist, setze es auf PERIOD_CURRENT pf=(int)MathFloor(_tf/timeframe);// Koeffizientenberechnung für PLOT_DRAW_BEGIN, PLOT_SHIFT und die Anzahl der zu berechnenden Bar. draw_begin=maPeriod*pf;// berechnen von PLOT_DRAW_BEGIN draw_shift=Shift*pf;// berechnen von PLOT_SHIFT //---- zum Zeichnen der Indikatorpuffer SetIndexBuffer(0,ExtMA,INDICATOR_DATA); //--- PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,draw_begin-pf); //Setzen der ersten Bar für das Zeichnen des Indikators PlotIndexSetInteger(0,PLOT_SHIFT,draw_shift); //Versatz der Linie beim Zeichnen PlotIndexSetString(0,PLOT_LABEL,"MA("+string(tf)+" "+string(maPeriod)+")");//Name im DatenFenster //--- MA_Handle=iMA(NULL,_tf,maPeriod,0,InpMAMethod,InpAppliedPrice); //abrufen des Handles des MAs if(MA_Handle==INVALID_HANDLE) { Print("getting MA Handle is failed! Error",GetLastError()); return(INIT_FAILED); } //--- Einstellen der Genauigkeit IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- Minimum der Bars für die Berechnung ExtBarsMinimum=draw_begin+draw_shift;// Berechnen der Mindestanzahl der Bars für die Berechnung //--- Initialisierung beendet return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Funktion des nutzerdefinierten Indikators | //+------------------------------------------------------------------+ 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[]) { //--- Prüfen von rates_total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation int limit; //--- Anwenden des Index der Zeitreihen auf die Arrayelemente ArraySetAsSeries(time,true); ArraySetAsSeries(ExtMA,true); //--- Ermitteln des Anfangspunktes //--- der Berechnungen für den benötigten Umfang der Daten, die zu kopieren sind //--- und dem Anfangsindex 'limit' der Bars für die Schleife der Neuberechnung if(prev_calculated>rates_total || prev_calculated<=0|| calculated!=bars_calculated)// Prüfen auf den Erststart der Indikatorberechnung { limit=rates_total-ExtBarsMinimum-1; // Startindex für die Berechnung aller Bars } else { limit=(rates_total-prev_calculated)+pf+1; // Startindex für die Berechnung der neuen Bars } if(Bars_Calculated!=0) limit=MathMin(Bars_Calculated,limit);
Wir werden nicht nach dem Index einer Bar suchen (iBarShift()), sondern die Werte direkt zeitorientiert kopieren.
//--- Hauptschleife for(int i=limit;i>=0 && !IsStopped();i--) { ExtMA[i]=_CopyBuffer(MA_Handle,time[i]); } //--- bars_calculated=calculated; Verwenden Sie hier die oben erwähnten Funktion. //+--------- CopyBuffer MA Handle ----------------------------------+ double _CopyBuffer(int handle,datetime start_time) { double buf[]; if(CopyBuffer(handle,0,start_time,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+-------------------- ENDE ----------------------------------------+
Hier ist das Ergebnis:
Abb. 10. MTF-Indikatorlinie
Diese Methode kann für alle Arten von linearen Indikatoren verwendet werden. Der größte Nachteil ist offensichtlich aus dem obigen Screenshot: die Stufenform. Für MA ist ein solches Verhalten kein Problem und kann sogar nützlich sein, da es eine klare Definition von Unterstützungs- und Widerstandsstufen ermöglicht. Aber für Oszillatoren, in denen wir Muster verwenden, behindert dieses Verhalten die Identifizierung und Zeichnung solcher Muster. Darüber hinaus ist die Lösung für WPR, CCI und ähnliche Indikatoren inakzeptabel, da sich das Linienbild bis zur Unkenntlichkeit verändert.
Um dieses Problem zu lösen, sollte der letzte Balken unter Berücksichtigung der Gewichtungsfaktoren berechnet werden. Fügen wir die globale Variable 'Interpolate' hinzu, die die Verwendung beider Lösungen ermöglicht.
input bool Interpolate = true; //--- Hauptschleife for(int i=limit;i>=0 && !IsStopped();i--) { int n; datetime t=time[i]; ExtMA[i]=_CopyBuffer(MA_Handle,t); if(!Interpolate) continue; //--- datetime times= _iTime(t); for(n = 1; i+n<rates_total && time[i+n]>= times; n++) continue; double factor=1.0/n; for(int k=1; k<n; k++) ExtMA[i+k]=k*factor*ExtMA[i+n]+(1.0-k*factor)*ExtMA[i]; } //---
In dieser Variante müssen wir die Funktion _iTime hinzufügen, die die Nummer des Balkens basierend auf der Öffnungszeit für das aktuelle Diagramm bestimmt.
//+------------------------------------------------------------------+ datetime _iTime(datetime start_time) { if(start_time < 0) return(-1); datetime Arr[]; if(CopyTime(NULL,_tf, start_time, 1, Arr)>0) return(Arr[0]); else return(-1); } //+------------------------------------------------------------------+
Das sieht jetzt wie eine normale Linie aus.
Abb. 11. MTF-Indikatorlinie mit _iTime
Auch wenn die Entwicklung solcher komplexen und ressourcenintensiven Systeme unangemessen erscheint, haben sie ihre Nachteile und können sogar unerlässlich sein. Bei Verwendung der klassischen Mittelung (MA, Alligator etc.) kann die Erhöhung des Berechnungszeitraums zu einer Verzögerung gegenüber der MTF-Version führen. Dies macht sich besonders bei kleinen Perioden bemerkbar.
Abb. 12. Verzögerung des Indikators MTF-MAs
Abb. 13. Verzögerung des MTF Stochastic Indikators
Bei einfachen Indikatoren wie den MAs und dem Alligator ist dies möglicherweise nicht so signifikant. Aber für komplexe Systeme, die aus zwei oder mehr MAs bestehen, wie MACD, AO, etc. kann dies von Bedeutung sein. Außerdem besteht keine Möglichkeit, die den Glättungszeitraum für AO oder AC zu ändern. Bei Indikatoren mit einer nicht glättenden Linie (WPR, CCI, etc.) kann eine geringfügige Erhöhung des Berechnungszeitraums kein zufriedenstellendes Ergebnis liefern, da sie sehr verrauscht sind.
Abb. 14. Der MTF-Indikator WPR
Abb. 15. Der MTF-Indikator WPR
Die Abbildungen 14-15 zeigen, dass sie erfolgreich zur Glättung eingesetzt werden können, wenn diese Möglichkeit im Algorithmus nicht vorgesehen ist.
Zusätzlich zu seiner direkten Funktion kann dieser Typ die Mängel des visuellen Testmodus im MetaTrader 5 Strategy Tester ausgleichen. Bei der Erstellung von MTF Expert Advisors für den Handel oder der Analyse der Wirksamkeit dieser Art von Strategie können wir nicht gleichzeitig die Position von Indikatoren aus verschiedenen TFs auf dem Bildschirm beobachten. Nach dem Testen erhalten wir eine Reihe von Registerkarten, abhängig von der Anzahl der verwendeten Perioden. Betrachten wir das Beispiel der Strategie "Elders Drei Bildschirme" aus dem Artikel Das MQL5-Kochbuch: Entwickeln eines Grundgerüsts für ein Handelssystem auf Basis der Drei-Bildschirme-Strategie von Anatoli Kazharski. Die Idee der Strategie ist wie folgt: Der erste Zeitrahmen ist der größte, wie wöchentlich, täglich oder 4 Stunden. Es wird verwendet, um den Haupttrend zu bestimmen. Der zweite Zeitrahmen unterscheidet sich von dem ersten um 1 oder 2 Grade. Sie wird zur Bestimmung der Korrektur verwendet. Der dritte Zeitrahmen unterscheidet sich um einen weiteren Grad. Es wird verwendet, um den optimalen Einstiegspunkt zu bestimmen.
Starten Sie im ersten Bildschirm, der normalerweise M30-W1 ist, den MACD (12,26,1) und den EMA mit einer Periodenlänge von 13. Im zweiten Bildschirm, М5-D1, starten Sie den stochastischen Oszillator (5,3,3,3). Der dritte Bildschirm von M1 bis H4 dient zur Platzierung von Stop-Orders in der Haupttrendrichtung.
Abb. 16. Elders Drei Bildschirme
Der Autor des Artikels hielt sich nicht an diese Variante, aber das Konzept der "Drei Bildschirme" blieb erhalten. Was wir während des Testprozesses und danach sehen:
Abb. 17. Der Test der Strategie von Elders Drei Bildschirme
Diese Variante erlaubt es uns nicht, die Funktionsweise des Experten Beraters (Strategie) richtig zu analysieren.
Lassen Sie uns mit unseren Tools eine eigene Version des EAs erstellen. Es wird der klassischen Strategieversion sehr ähnlich sein. Auch die Erstellung von Expert Advisors auf der Grundlage der oben genannten Indikatoren ist den klassischen EAs sehr ähnlich. Hier ist der Hauptteil des Codes.
#define EXPERT_MAGIC 1234502 //--- #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include <Trade\OrderInfo.mqh> //+------------------------------------------------------------------+ enum compTF { A, //m1-m5-m15 B, //m5-m15-h1 C, //m15-h1-h4 E //h1-h4-d1 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input string s0="//--- Eingabeparameter Losgröße+Trailing ---//"; input double InpLots =0.1; // Losgröße input int InpTakeProfit =150; // Take-Profit (in Pips) input int InpStopLoss =60; // Stop-Loss (in Pips) input int InpLevel_S =20; // Signallevel input compTF InpTFreg =2; input string s1="//--- Eingabeparameter MA ---//"; input int Signal_MA_PeriodMA =21; // Glättungslänge input string s2="//--- Eingabeparameter MACD ---//"; input int Signal_MACD_PeriodFast =12; // Periodenlänge des schnellen MA input int Signal_MACD_PeriodSlow =26; // Periodenlänge des langsamen MA input string s3="//--- Eingabeparameter der Stochastic ---//"; input int Signal_Stoch_PeriodK =5; // K-Periode input int Signal_Stoch_PeriodD =3; // D-Periode input string s4="//--- Eingabeparameter Trailing ---//"; //--- Eingabeparameter für das Trailing input int InpTrailingStop =25; // Trailing Stop-Level (in Pips) input int InpOffset =5; // Abstand vom Preis (in Pips) //--- int ExtTimeOut=10; // Zeitspanne zwischen den Handelsoperationen in Sekunden int barsCalculated=3; datetime t=0; datetime time[]; //+------------------------------------------------------------------+ //| AC Beispielklasse des Experten | //+------------------------------------------------------------------+ class CSampleExpert { protected: double m_adjusted_point; // 3- oder 5-stellige Werte CTrade m_trade; // Handelsobjekt CSymbolInfo m_symbol; // Symbolinformation CPositionInfo m_position; // Handelsposition CAccountInfo m_account; // Kontoinformation COrderInfo m_order; // Auftragsinformation //--- Indikatoren ENUM_TIMEFRAMES mas_tf[3]; // Array der Zeitrahmen int handle_MACD; // Handle des MACD int handle_MA; // Handle des MA int handle_Stochastic; // Handle der Stochastik //--- Indikatorpuffer double macd_buff[]; // Hauptpuffer des MACD double ma_buff[]; // Hauptpuffer des MA double stoch_buff[]; // Hauptpuffer der Stochastic //--- double close[]; double open[]; double low[]; double high[]; //--- zu bearbeitenden Indikatordaten double macd_ind_0; double macd_ind_1; double ma_ind; double stoch_ind_0; double stoch_ind_1; int level_stoch; //--- zu verarbeitenden Daten des Trailing-Stop double m_traling_stop; double m_take_profit; double m_stop_losse; public: CSampleExpert(void); ~CSampleExpert(void); bool Init(void); void Deinit(void); bool Processing(void); protected: bool InitCheckParameters(const int digits_adjust); bool Copy(void); // bool InitIndicators(void); bool LongModified(void); bool ShortModified(void); bool LongOpened(void); // Prüfen der Kaufbedingungen bool ShortOpened(void); // Prüfen der Verkaufsbedingungen bool OpenSellStop(void); // platzieren einer SELLSTOP-Order bool OpenBuyStop(void); // platzieren einer BUYSTOP-Order bool OrderModifySellStop(void); // Ändern einer SELLSTOP-Order bool OrderModifyBuyStop(void); // Ändern einer BUYSTOP-Order bool DellSellStop(void); // Löschen einer SELLSTOP-Order bool DellBuyStop(void); // Löschen einer BUYSTOP-Order }; //--- globaler Experte CSampleExpert ExtExpert; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CSampleExpert::CSampleExpert(void) : m_adjusted_point(0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(0), macd_ind_1(0), stoch_ind_0(0), stoch_ind_1(0), ma_ind(0), m_traling_stop(0), m_take_profit(0) { ArraySetAsSeries(macd_buff,true); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSampleExpert::~CSampleExpert(void) { } //+------------------------------------------------------------------+ //| Initialisierung und Verifikation der Eingabeparameter | //+------------------------------------------------------------------+ bool CSampleExpert::Init(void) { //--- Initialisierung der allgemeinen Informationen m_symbol.Name(Symbol()); // Symbol m_trade.SetExpertMagicNumber(EXPERT_MAGIC); // Magicnummer m_trade.SetMarginMode(); m_trade.SetTypeFillingBySymbol(Symbol()); //--- Bestimmen von 3 oder 5 Stellen int digits_adjust=1; if(m_symbol.Digits()==3 || m_symbol.Digits()==5) digits_adjust=10; m_adjusted_point=m_symbol.Point()*digits_adjust; //--- Einstellungen der standardmäßigen Differenzen für den Handel m_traling_stop =InpTrailingStop*m_adjusted_point; m_take_profit =InpTakeProfit*m_adjusted_point; m_stop_losse =InpStopLoss*m_adjusted_point; //--- Einstellungen der standardmäßigen Differenzen für den Handel in angepassten Punkten m_trade.SetDeviationInPoints(3*digits_adjust); //--- int x=InpTFreg; switch(x) { case 0: {mas_tf[0]=PERIOD_M1;mas_tf[1]=PERIOD_M5;mas_tf[2]=PERIOD_M15;} break; case 1: {mas_tf[0]=PERIOD_M5;mas_tf[1]=PERIOD_M15;mas_tf[2]=PERIOD_H1;} break; case 2: {mas_tf[0]=PERIOD_M15;mas_tf[1]=PERIOD_H1;mas_tf[2]=PERIOD_H4;} break; case 3: {mas_tf[0]=PERIOD_H1;mas_tf[1]=PERIOD_H4;mas_tf[2]=PERIOD_D1;} break; } //--- if(!InitCheckParameters(digits_adjust)) return(false); if(!InitIndicators()) return(false); //--- Ergebnis return(true); } //+------------------------------------------------------------------+ //| Verifikation der Eingabeparameter | //+------------------------------------------------------------------+ bool CSampleExpert::InitCheckParameters(const int digits_adjust) { //--- Prüfen der Quelldaten if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { printf("Take Profit must be greater than %d",m_symbol.StopsLevel()); return(false); } if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel()) { printf("Trailing Stop must be greater than %d",m_symbol.StopsLevel()); return(false); } //--- Prüfen der Losgröße if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax()) { printf("Lots must be in the range between %f and %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>1.0E-10) { printf("The amount does not correspond to the lot step %f",m_symbol.LotsStep()); return(false); } //--- Warnungen if(InpTakeProfit<=InpTrailingStop) printf("Warning: Trailing Stop must be less than Take Profit"); //--- Ergebnis return(true); } //+------------------------------------------------------------------+ //| Initialisierung der Indikatoren | //+------------------------------------------------------------------+ bool CSampleExpert::InitIndicators(void) { //--- Erstellen des MACD if(handle_MACD==INVALID_HANDLE) { //--- handle_MACD=iCustom(NULL,0,"MTF\\Oscillators\\MTF_MACD",mas_tf[2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow); //--- if(handle_MACD==INVALID_HANDLE) { printf("Error occurred while creating MACD"); return(false); } } //--- Erstellen des MA if(handle_MA==INVALID_HANDLE) { //--- handle_MA=iCustom(NULL,0,"MTF\\Trend\\MA_MultiTF",mas_tf[2],Signal_MA_PeriodMA,0,MODE_EMA,PRICE_CLOSE); //--- if(handle_MA==INVALID_HANDLE) { printf("Error occurred while creating MA"); return(false); } } //--- Erstellen der Stochastik if(handle_Stochastic==INVALID_HANDLE) { //--- handle_Stochastic=iCustom(NULL,0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD); //--- if(handle_Stochastic==INVALID_HANDLE) { printf("Error occurred while creating Stochastic"); return(false); } } //--- Ergebnis return(true); } //+------------------------------------------------------------------+ //| Ändern der Kaufposition | //+------------------------------------------------------------------+ bool CSampleExpert::LongModified(void) { bool res=false; //--- Prüfen des Trailing-Stops if(InpTrailingStop>0) { if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop) { double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()<sl || m_position.StopLoss()==0.0) { //--- Ändern der Position if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Long position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- wurde geändert und muss den Expert Advisor verlassen res=true; } } } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Ändern der Verkaufsposition | //+------------------------------------------------------------------+ bool CSampleExpert::ShortModified(void) { bool res=false; //--- Prüfen des Trailing-Stops if(InpTrailingStop>0) { if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop)) { double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()>sl || m_position.StopLoss()==0.0) { //--- Ändern der Position if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Short position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- wurde geändert und muss den Expert Advisor verlassen res=true; } } } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Prüfen der Bedingungen zum Öffnen von Kaufpositionen | //+------------------------------------------------------------------+ bool CSampleExpert::LongOpened(void) { bool res=false; level_stoch=InpLevel_S; //--- Prüfen der Möglichkeit eine Kaufposition (BUY) zu öffnen if(stoch_ind_1<level_stoch && stoch_ind_0>level_stoch && macd_ind_1>macd_ind_0 && ma_ind<close[1] && ma_ind<open[1])//&& ma_ind<close[1] && ma_ind<open[1] { //--- verlassen des EAs res=true; } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Öffnen einer Buy-Stop-Order | //+------------------------------------------------------------------+ bool CSampleExpert::OpenBuyStop(void) { bool res=false; double tp=0,sl=0; //--- if(LongOpened()) { res=true; //--- Deklarieren und Initialisieren der Anforderung und des Ergebnisses MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- Parameter der Pending-Orders request.action =TRADE_ACTION_PENDING; // Typ der Handelsoperation request.symbol =Symbol(); // Symbol request.deviation=5; // erlaubte Abweichung vom Preis request.volume =InpLots; // Volumen in Lots request.magic =EXPERT_MAGIC; // MagicNummer der Aufträge double offset=InpOffset; // Abstand in Punkten vom aktuellen Preis zum Platzieren der Aufträge double price; // Auslösepreis der Aufträge double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // Punktgröße int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // Anzahl der Dezimalstellen (digits) //--- operation type request.type=ORDER_TYPE_BUY_STOP; // Typ des Auftrags price=high[1]+offset*m_adjusted_point; // Eröffnungspreis request.price=NormalizeDouble(price,digits); // Normalisierter Eröffnungspreis tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // neuen Stop-Loss zu Struktur hinzufügen request.tp =NormalizeDouble(tp,_Digits); // neuen Take-Profit zu Struktur hinzufügen //--- Anforderung senden if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // wenn der Auftrag nicht erfolgreich gesendet wurde, ausdrucken der Fehlernummer //--- Information über die Operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- verlassen des EAs } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Prüfen der Bedingungen für das Öffnen einer Verkaufsposition | //+------------------------------------------------------------------+ bool CSampleExpert::ShortOpened(void) { bool res=false; level_stoch=100-InpLevel_S; //--- Prüfen der Möglichkeit zu verkaufen (SELL) if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[1] && ma_ind>open[1]) { //--- verlassen des EAs res=true; } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Öffnen einer Sell-Stop-Order | //+------------------------------------------------------------------+ bool CSampleExpert::OpenSellStop(void) { bool res=false; double tp=0,sl=0; //--- if(ShortOpened()) { res=true; //--- Deklarieren und Initialisieren der Anforderung und des Ergebnisses MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- Parameter der Pending-Orders request.action =TRADE_ACTION_PENDING; // Typ der Handelsoperation request.symbol =Symbol(); // Symbol request.deviation=5; // erlaubte Abweichung vom Preis request.volume=InpLots; // Volumen in Lots request.magic=EXPERT_MAGIC; // MagicNummer des Auftrags int offset=InpOffset; // Abstand in Punkten vom aktuellen Preis zum Platzieren der Aufträge double price; // Auslösepreis der Aufträge int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // Anzahl der Dezimalstellen (digits) request.type=ORDER_TYPE_SELL_STOP; // Ordertyp price=low[1]-offset*m_adjusted_point; // Eröffnungspreis request.price=NormalizeDouble(price,digits); // Normalisierter Eröffnungspreis tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // neuen Stop-Loss zu Struktur hinzufügen request.tp =NormalizeDouble(tp,_Digits); // neuen Take-Profit zu Struktur hinzufügen //--- Anforderung senden if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // wenn der Auftrag nicht erfolgreich gesendet wurde, ausdrucken der Fehlernummer //--- Information über die Operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- verlassen des EAs } //--- Ergebnis return(res); } //+------------------------------------------------------------------+ //| Die Hauptfunktion liefert 'true', wenn eine der Positionen | //| bearbeitet wurde | //+------------------------------------------------------------------+ bool CSampleExpert::Processing(void) { // MqlDateTime dt; //--- Aktualisierungsfrequenz if(!m_symbol.RefreshRates()) return(false); //--- Aktualisierung des Indikators if(BarsCalculated(handle_Stochastic)<barsCalculated) return(false); if(CopyBuffer(handle_Stochastic,0,0,barsCalculated,stoch_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MACD)<barsCalculated) return(false); if(CopyBuffer(handle_MACD,0,0,barsCalculated,macd_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MA)<barsCalculated) return(false); if(CopyBuffer(handle_MA,0,0,barsCalculated,ma_buff)!=barsCalculated) return(false); //--- if(!Copy())return(false); //--- Programmvereinfachung und ein schnellerer Zugriff //--- die Daten werden in internen Variablen gespeichert macd_ind_0 = macd_buff[1]; macd_ind_1 = macd_buff[0]; ma_ind = ma_buff[1]; stoch_ind_0 = stoch_buff[1]; stoch_ind_1 = stoch_buff[0]; //--- es ist wichtig, alles richtig zu verlassen ... //--- Zuerst die Prüfung, ob Positionen existieren - Versuch sie auszuwählen bool bord=false,sord=false; ulong ticket; //+--- Es gibt offene Positionen und Trailing-Stop ist aktiviert --------+ if(m_position.Select(Symbol()) && PositionsTotal()>0 && m_traling_stop!=0) { if(m_position.PositionType()==POSITION_TYPE_BUY) { bord=true; //--- Ändern einer Kaufposition if(LongModified()) return(true); } else { sord=true; //--- Ändern einer Verkaufsposition if(ShortModified()) return(true); } } //+--------- Handels-STOP-Orders ------------------------------------+ // In dieser Schleife werden alle platzierten Pending-Order einzeln überprüft for(int i=0;i<OrdersTotal();i++) { // auswählen jedes Auftrags und Abfragen seines Tickets ticket=OrderGetTicket(i); // Bearbeiten der Buy-Stop-Orders if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { // Einstellen des Flags für die Existenz eines Buy-Stop bord=true; //--- Es ist wichtig, den Markt korrekt zu betreten, verschieben falls notwendig des Auftrags if(bord)OrderModifyBuyStop(); // modify the BUYSTOP order } // Bearbeitung des Sell-Stop if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { // Einstellen des Flags für die Existenz eines Sell-Stop sord=true; //--- Es ist wichtig, den Markt korrekt zu betreten, verschieben falls notwendig des Auftrags if(sord)OrderModifySellStop(); // Ändern der SELLSTOP-Order } } //--- Wenn es keine Aufträge gibt, platziere sie -------------------+ if(!sord)if(OpenSellStop()){sord=true;return(true);} if(!bord)if(OpenBuyStop()){bord=true;return(true);} //--- verlassen ohne Positionsbearbeitung return(false); } //+------------------------------------------------------------------+ //|Ändern der Parameter eine bereits platzierten Sell-Stop-Order | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifySellStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // Abstand in Punkten vom aktuellen Preis zum Platzieren der Aufträge double price; // Auslösepreis der Aufträge int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // Anzahl der Dezimalstellen (digits) // declare and initialize the trade request and result MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // Anzahl der Pending-Orders //--- Iterieren über alle platzierten Pending-Orders for(int i=total-1; i>=0; i--) { // auswählen jedes Auftrags und Abfragen seines Tickets ticket=OrderGetTicket(i); // Bearbeitung des Sell-Stop if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNummer der Order //--- falls die MagicNummer passt if(magic==EXPERT_MAGIC) { price=low[1]-offset*m_adjusted_point; // Eröffnungspreis if(price>m_order.PriceOpen()) // Prüfen der Bedingungen if(low[1]>low[2]) // Prüfen der Bedingungen { request.action=TRADE_ACTION_MODIFY; // Typ der Handelsoperation request.order = OrderGetTicket(i); // Ticket der Order request.symbol =Symbol(); // Symbol request.deviation=InpOffset; // erlaubte Abweichung vom Preis price=low[1]-offset*m_adjusted_point; // Eröffnungspreis request.price=NormalizeDouble(price,digits); // normalisierter Eröffnungspreis tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // neuen Stop-Loss zu Struktur hinzufügen request.tp =NormalizeDouble(tp,_Digits); // neuen Take-Profit zu Struktur hinzufügen //--- Anforderung senden if(!OrderSend(request,result)) { // Einstellen des Flags dafür, dass die Sell-Stop-Order nicht gelöscht wurde res=true; printf("OrderSend error %d",GetLastError()); } // wenn der Auftrag nicht erfolgreich gesendet wurde, ausdrucken der Fehlernummer //--- Information über die Operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //|Ändern der Parameter eine bereits platzierten Buy-Stops-Order | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifyBuyStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // Abstand in Punkten vom aktuellen Preis zum Platzieren der Aufträge double price; // Auslösepreis der Aufträge int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // Anzahl der Dezimalstellen (digits) //--- Deklarieren und Initialisieren der Anforderung und des Ergebnisses MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // Anzahl der Pending-Orders //--- Iterieren über alle platzierten Pending-Orders for(int i=total-1; i>=0; i--) { // auswählen jedes Auftrags und Abfragen seines Tickets ticket=OrderGetTicket(i); // Bearbeiten der Buy-Stop-Orders if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNummer der Order //--- falls die MagicNummer passt if(magic==EXPERT_MAGIC) { price=high[1]+offset*m_adjusted_point; // Eröffnungspreis if(price<m_order.PriceOpen()) // Prüfen der Bedingungen { request.action=TRADE_ACTION_MODIFY; // Typ der Handelsoperation request.symbol =Symbol(); // Symbol request.action=TRADE_ACTION_MODIFY; // Typ der Handelsoperation request.order = OrderGetTicket(i); // Ticket der Order request.symbol =Symbol(); // Symbol request.deviation=InpOffset; // erlaubte Abweichung vom Preis //--- Setzen von Preis, Take-Profit und Stop-Loss request.price=NormalizeDouble(price,digits); // normalisierter Eröffnungspreis tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // neuen Stop-Loss zu Struktur hinzufügen request.tp =NormalizeDouble(tp,_Digits); // neuen Take-Profit zu Struktur hinzufügen //--- Anforderung senden if(!OrderSend(request,result)) { // Einstellen des Flags dafür, dass die Buy-Stop-Order nicht gelöscht wurde res=true; printf("OrderSend error %d",GetLastError()); } // wenn der Auftrag nicht erfolgreich gesendet wurde, ausdrucken der Fehlernummer //--- Information über die Operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit(void) { //--- Erstellen aller benötigten Objekte if(!ExtExpert.Init()) return(INIT_FAILED); //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Funktion für die Tickbearbeitung | //+------------------------------------------------------------------+ void OnTick(void) { static datetime limit_time=0; // der Zeitpunkt der letzten Berechnungen + timeout //--- keine Beachtung des Timeout if(TimeCurrent()>=limit_time) { //--- Datenprüfung if(Bars(Symbol(),Period())>barsCalculated) { //--- Prüfen der Zeitbegrenzung bezüglich des Timeouts in Sekunden, wenn zu verarbeitenden if(ExtExpert.Processing()) limit_time=TimeCurrent()+ExtTimeOut; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSampleExpert::Copy(void) { //--- zu kopierender Umfang int copied=3; //--- ArrayResize(high,copied); ArrayResize(low,copied); ArrayResize(close,copied); ArrayResize(open,copied); //+------- Festlegen der Richtung der Indices der Arrays ------------+ ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(open,true); //--- kopieren der Preis-Bars if(CopyHigh(NULL,mas_tf[0],0,copied,high)<0) {printf("No copied High",GetLastError());return(false);} //--- if(CopyLow(NULL,mas_tf[0],0,copied,low)<0) {printf("No copied Low",GetLastError());return(false);} //--- if(CopyClose(NULL,mas_tf[2],0,copied,close)<0) {printf("No copied Close",GetLastError());return(false);} //--- if(CopyOpen(NULL,mas_tf[2],0,copied,open)<0) {printf("No copied Open",GetLastError());return(false);} //--- return(true); } //+------------------------------------------------------------------+
Die Verwendung dieses EAs ist viel einfacher:
Abb. 18. EA-Test mit den implementierten MTF-Tools
Schlussfolgerung
Obwohl die Autoren der MQL5-Softwaremodule keine direkte Möglichkeit bieten, solche Algorithmen zu erstellen, können diese Indikatoren sehr nützlich sein. In einigen Fällen ermöglichen sie die gleichzeitige Marktzustandsanalyse bei verschiedenen TFs, die Verbesserung der Effizienz des Strategietesters und die Verbesserung der Glättung. Die vorgestellten Codebeispiele können Nachteile haben, so dass es viele Möglichkeiten zur Weiterentwicklung der Idee über die MTF-Indikatoren mit der Programmiersprache MQL5 gibt.
Anlagen
Name | Typ | Ort |
---|---|---|
MA_MultiPeriod | Trend | MQL5\Indicators\MA_MultiPeriod.mq5 |
MA_MultiTF | Trend | MQL5\Indicators\MTF\Trend\MA_MultiTF.mq5 |
MTF_BB | Trend | MQL5\Indicators\MTF\Trend\MTF_BB.mq5 |
MTF_Envelopes | Trend | MQL5\Indicators\MTF\Trend\MTF_Envelopes.mq5 |
MTF_ParabolicSAR | Trend | MQL5\Indicators\MTF\Trend\MTF_ParabolicSAR.mq5 |
MTF_StdDev | Trend | MQL5\Indicators\MTF\Trend\MTF_StdDev.mq5 |
MTF_CCI | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_CCI.mq5 |
MTF_Force_Index | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_Force_Index.mq5 |
MTF_MACD | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_MACD.mq5 |
MTF_Momentum | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_Momentum.mq5 |
MTF_RSI | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_RSI.mq5 |
MTF_RVI | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_RVI.mq5 |
MTF_Stochastic | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_Stochastic.mq5 |
MTF_WPR | Oszillatoren | MQL5\Indicators\MTF\Oscillators\MTF_WPR.mq5 |
Triple Screen Trading System 1.0 | Expert Advisor |
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2837
- 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.