Wie man eine Handelsstrategie in MetaTrader 5 schnell entwickeln und debuggen kann
Automatische Scalping-Systeme gelten zurecht als der Höhepunkt des algorithmischen Tradings, aber es ist auch am kompliziertesten, einen Code für diese Systeme zu schreiben. In diesem Artikel zeigen wir, wie man mithilfe von eingebauten Werkzeugen für Debugging und visuelles Testen Strategien entwickeln kann, die auf der Analyse eingehender Ticks basieren. Um Regeln für Einstieg und Ausstieg zu erarbeiten, braucht man häufig jahrelang manuellen zu handeln. Aber mithilfe von MetaTrader 5 können Sie jede solche Strategie anhand realer historischer Daten schnell testen.
Handelsidee basierend auf Ticks
Zunächst einmal brauchen wir einen Indikator zu erstellen, der Tickcharts zeichnen wird, d.h. Charts, auf welchen jede Preisveränderung zu sehen ist. Einen der ersten solcher Indikatoren können Sie in der Code Base finden — https://www.mql5.com/de/code/89. Im Vergleich zu gewöhnlichen Charts müssen Tick-Charts beim Eintreffen eines neuen Ticks nach hinten verschoben werden.
Als Grundlage der Idee nehmen wir eine Reihe der Preisveränderungen zwischen zwei aufeinanderfolgenden Ticks. Die Reihenfolge wird ungefähr so aussehen:
+1, 0, +2, -1, 0, +1, -2, -1, +1, -5, -1, +1, 0, -1, +1, 0, +2, -1, +1, +6, -1, +1,...
Das Gesetz der Normalverteilung lautet, dass 99% der Preisveränderungen zwischen zwei Ticks innerhalb von 3 Sigma erfolgen. Wir werden die Standardabweichung auf jedem Tick in der Echtzeit berechnen und sprunghafte Preisveränderungen mit rot und blau markieren. Auf diese Weise versuchen wir eine Strategie für die Verwendung solcher Abweichungen visuell auszuwählen: in der Richtung der Änderung handeln oder "Rückkehr zum Mittelwert" nutzen. Wie Sie sehen können, ist die Idee ganz einfach, und diesen Weg sind bestimmt die meisten Mathematikliebhaber gegangen.
Erstellung des Tick-Indikators
Starten wir MQL Wizard in MetaEditor, geben wir einen Namen und zwei Input-Parameter ein:
- ticks — wie viele Ticks für die Berechnung der Standardabweichung verwendet werden
- gap — Koeffizient für die Berechnung des Intervalls in Sigma.
Weiter aktivieren wir die Option "Indikator in separatem Fenster" und geben zwei grafische Objekte an, welche Informationen in einem Unterfenster anzeigen werden: Linien für Ticks und farbige Pfeile für Signale, um sprunghafte Preisbewegungen zu markieren.
Nehmen wir die gelb markierten Änderungen in der Vorlage vor
//+------------------------------------------------------------------+ //| TickSpikeHunter.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 2 //--- plot TickPrice #property indicator_label1 "TickPrice" #property indicator_type1 DRAW_LINE #property indicator_color1 clrGreen #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot Signal #property indicator_label2 "Signal" #property indicator_type2 DRAW_COLOR_ARROW #property indicator_color2 clrRed,clrBlue,C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0' #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- input parameters input int ticks=50; // Anzahl der Ticks in Berechnungen input double gap=3.0; // Breite des Kanals in Sigma //--- indicator buffers double TickPriceBuffer[]; double SignalBuffer[]; double SignalColors[]; //--- Zähler der Preisänderungen int ticks_counter; //--- erster Aufruf des Indikators bool first; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA); SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX); //--- geben wir leere Werte ein, die beim Zeichnen ignoriert werden müssen PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); //--- wir werden Signale als dieses Zeichen ausgeben PlotIndexSetInteger(1,PLOT_ARROW,159); //--- Initialisierung globaler Variablen ticks_counter=0; first=true; //--- erfolgreiche Initialisierung des Programms return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Nun fügen wir den vordefinierten Handler eingehender Ticks OnCalculate() in den Code hinzu. Beim ersten Aufruf der Funktion setzen wir die Werte in den Indikator-Puffern auf Null und legen wir für sie einfachheitshalber ein Zeichen der Zeitreihe fest. Dadurch werden sie von rechts nach links indiziert. Dies erlaubt es, auf den letzten Wert des Indikatorpuffers nach dem Index 0 zuzugreifen, d.h. der Wert des letzten Ticks wird in TickPriceBuffer[0] gespeichert.
Die Verarbeitung von Ticks erfolgt in einer separaten Funktion ApplyTick():
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- beim ersten Aufruf setzen wir die Indikator-Puffer auf Null und legen wir ein Zeichen der Zeitreihe fest if(first) { ZeroMemory(TickPriceBuffer); ZeroMemory(SignalBuffer); ZeroMemory(SignalColors); //--- die Arrays der Zeitreihe folgen von vorne nach hinten, in diesem Fall ist das besser ArraySetAsSeries(SignalBuffer,true); ArraySetAsSeries(TickPriceBuffer,true); ArraySetAsSeries(SignalColors,true); first=false; } //--- nehmen wir den aktuellen Close Wert als Preis double lastprice=close[rates_total-1]; //--- zählen wir die Ticks ticks_counter++; ApplyTick(lastprice); // Berechnungen und Verschiebung in den Puffern //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| verwendet Tick für Berechnungen | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); //--- speichern wir den letzten Wert TickPriceBuffer[0]=price; //--- }
Die Funktion ApplyTick() verschiebt alle Pufferwerte um eine Position in die Historie zurück und speichert den letzten Tick in TickPriceBuffer[0]. Starten wir den Indikator im Debug-Modus und beobachten wir ihn eine Weile.
Wie wir sehen, bleibt der Bid-Preis, anhand welchen Close der aktuellen Kerze gebildet wird, häufig unverändert. Aus diesem Grund wird der Chart als "Plateau" gezeichnet. Korrigieren wir ein bisschen den Code, damit die Indikatorwerte in Form einer "Säge" dargestellt werden. So ist der Chart visuell besser wahrzunehmen.
//--- berechnen, nur wenn sich der Preis geändert hat if(lastprice!=TickPriceBuffer[0]) { ticks_counter++; // Ticks zählen ApplyTick(lastprice); // Berechnungen und Verschiebung in den Puffern }
Wir haben die erste Version des Indikators erstellt, nun haben wir keine Null-Inkremente des Preises.
Fügen wir einen zusätzlichen Puffer und die Berechnung der Standardabweichung hinzu
Um die Abweichung zu berechnen brauchen wir ein zusätzliches Array, in welchem Preisinkremente auf jedem Tick gespeichert werden. Dafür fügen wir noch einen Indikatorpuffer und den entsprechenden Code an den richtigen Stellen hinzu:
#property indicator_separate_window #property indicator_buffers 4 #property indicator_plots 2 ... //--- indicator buffers double TickPriceBuffer[]; double SignalBuffer[]; double DeltaTickBuffer[]; double ColorsBuffers[]; ... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA); SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX); SetIndexBuffer(3,DeltaTickBuffer,INDICATOR_CALCULATIONS); ... } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const ...) //--- Indikatorpuffer beim ersten Aufruf auf Null setzen und das Zeichen der Zeitreihe festlegen if(first) { ZeroMemory(TickPriceBuffer); ZeroMemory(SignalBuffer); ZeroMemory(SignalColors); ZeroMemory(DeltaTickBuffer); //--- die Arrays der Zeitreihe folgen von vorne nach hinten, in diesem Fall ist das besser ArraySetAsSeries(TickPriceBuffer,true); ArraySetAsSeries(SignalBuffer,true); ArraySetAsSeries(SignalColors,true); ArraySetAsSeries(DeltaTickBuffer,true); first=false; } ... return(rates_total); } //+------------------------------------------------------------------+ //| verwendet Tick für Berechnungen | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1); //--- speichern wir den letzten Wert TickPriceBuffer[0]=price; //--- berechnen wir die Differenz mit dem vorherigen Wert DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- wir bekommen die Standardabweichung double stddev=getStdDev(ticks);
Jetzt können wir die Standardabweichung berechnen. Schreiben wir zuerst die Funktion getStdDev(), die alle Berechnungen direkt macht, indem sie so viele Zyklen durch alle Array-Elemente durchläuft, wie viele es nötig sind.
//+------------------------------------------------------------------+ //| berechnet die Standardabweichung "direkt" | //+------------------------------------------------------------------+ double getStdDev(int number) { double summ=0,sum2=0,average,stddev; //--- die Summe der Änderungen und die mathematische Erwartung berechnen for(int i=0;i<ticks;i++) summ+=DeltaTickBuffer[i]; average=summ/ticks; //--- berechnen wir jetzt die Standardabweichung sum2=0; for(int i=0;i<ticks;i++) sum2+=(DeltaTickBuffer[i]-average)*(DeltaTickBuffer[i]-average); stddev=MathSqrt(sum2/(number-1)); return (stddev); }
Weiter schreiben wir einen Block ebenda, der für das Platzieren von Signalen auf dem Tickchart zuständig ist: Setzten von roten und blauen Kreisen
//+------------------------------------------------------------------+ //| verwendet Tick für Berechnungen | //+------------------------------------------------------------------+ void ApplyTick(double price) { int size=ArraySize(TickPriceBuffer); ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1); ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1); ArrayCopy(SignalColors,SignalColors,1,0,size-1); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1); //--- speichern wir den letzten Wert TickPriceBuffer[0]=price; //--- berechnen wir die Differenz mit dem vorherigen Wert DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- wir bekommen die Standardabweichung double stddev=getStdDev(ticks); //--- wenn die Preisänderung den vorgegebenen Schwellenwert überschreitet if(MathAbs(DeltaTickBuffer[0])>gap*stddev) // wird beim ersten Tick das Signal angezeigt, lassen wir es als Eigenschaft { SignalBuffer[0]=price; // setzen wir einen Punkt string col="Red"; // standardmäßig wird der Punkt rot sein if(DeltaTickBuffer[0]>0) // wenn der Preis drastisch gestiegen ist, { SignalColors[0]=1; // dann einen blauen Punkt col="Blue"; // speichern um im Log anzuzeigen } else // der Preis ist drastisch gesunken SignalColors[0]=0; // roter Punkt //--- zeigen wir das im Journal an PrintFormat("tick=%G change=%.1f pts, trigger=%.3f pts, stddev=%.3f pts %s", TickPriceBuffer[0],DeltaTickBuffer[0]/_Point,gap*stddev/_Point,stddev/_Point,col); } else SignalBuffer[0]=0; // kein Signal //--- }
Drücken wir F5 (Debugging starten/fortsetzen) und beobachten wir im MetaTrader 5 Terminal, wie unser Indikator funktioniert.
Nun ist höchste Zeit den Code zu debuggen, um Fehler ausfindig zu machen und das Programm zu beschleunigen.
Profiling des Codes für die Beschleunigung des Programms
Für Programme, die in der Echtzeit funktionieren, ist die Ausführungsgeschwindigkeit von vorrangiger Bedeutung. Die Entwicklungsumgebung MetaEditor ermöglicht es, den Zeitaufwand für die Ausführung eines Programmabschnittes schnell und einfach einzuschätzen. Dafür muss man Profiling starten und das Programm eine Weile laufen lassen. Für das Profiling des Indikators reicht eine Minute.
Wie Sie sehen, nimmt die Verarbeitung der ApplyTick() Funktion, die 41 Male aus der OnCalculate() Funktion aufgerufen wurde, die meiste Zeit in Anspruch. Die Funktion OnCalculate() wurde 143 Male aufgerufen, aber der Preis auf dem eingegangenen Tick unterschied sich vom Preis auf dem vorherigen Tick nur in 41 Fällen. In der Funktion ApplyTick() nehmen die meiste Zeit die Aufrufe der Funktion ArrayCopy() in Anspruch, die nur zusätzliche Aufgaben lösen und keine Berechnungen ausführen, für welche dieser Indikator konzipiert wurde. Die Berechnung der Standardabweichung in der 111. Codezeile hat nur 0.57% der gesamten Ausführungszeit des Programms verbraucht.
Versuchen wir unproduktiven Aufwand zu reduzieren, dafür probieren wir nicht alle Array-Elemente (TickPriceBuffer usw.), sondern nur die letzten 200 zu kopieren. Denn es reicht uns, die letzten 200 Werte im Chart zu sehen, außerdem kann die Tickzahl innerhalb einer Handelssitzung Hunderttaudende Ticks erreichen. Es ist nicht nötig, sich alle anzuschauen. Aus diesem Grund geben wir den Inputparameter shift=200 ein, der die Zahl der zu verschiebenden Werte festlegt. Fügen Sie die gelb markierten Zeilen in den Code hinzu:
//--- input parameters input int ticks=50; // Anzahl der Ticks in den Berechnungen input int shift=200; // Anzahl der zu verschiebenden Werte input double gap=3.0; // Breite des Kanals in Sigma ... void ApplyTick(double price) { //--- wie viele Elemente auf jedem Tick in den Indiaktorpuffern verschoben werden int move=ArraySize(TickPriceBuffer)-1; if(shift!=0) move=shift; ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move); ArrayCopy(SignalBuffer,SignalBuffer,1,0,move); ArrayCopy(SignalColors,SignalColors,1,0,move); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move);
Starten wir das Profiling aufs Neue, und wir bekommen neue Ergebnisse: für das Kopieren von Arrays wird jetzt um das Tausendfache weniger Zeit benötigt, nun nimmt der Aufruf von StdDev() die meiste Zeit in Anspruch, die für die Berechnung der Standardabweichung zuständig ist.
Auf diese Weise haben wir die ApplyTick() Funktion beschleunigt, so sparen wir Ressourcen bei der Optimierung der Strategie und beim Laufen des Programms in der Echtzeit, denn es gibt nie zu viele Rechnerkapazitäten.
Analytische Code-Optimierung
Manchmal kann man sogar einen optimal geschriebenen Code noch schneller laufen lassen. In diesem Fall kann die Berechnung der Standardabweichung beschleunigt werden, wenn man die Formel ein wenig ändert.
Wir können also einfach die Quadratzahl der Summe und die Summe der Quadratzahlen der Preisinkrementen berechnen und dadurch die Zahl der mathematischen Operationen auf jedem Tick reduzieren. Auf jedem Tick subtrahieren wir das Element des Arrays und fügen das eingehende Element des Arrays den Variablen hinzu, welche die Summen beinhalten.
Erstellen wir eine neue Funktion getStdDevOptimized(), in welcher wir die bereits bekannte Methode der Verschiebung von Werten des Arrays innerhalb des Arrays verwenden.
//+------------------------------------------------------------------+ //| berechnet die Standardabweichung nach Formeln | //+------------------------------------------------------------------+ double getStdDevOptimized(int number) { //--- static double X2[],X[],X2sum=0,Xsum=0; static bool firstcall=true; //--- erster Aufruf if(firstcall) { //--- setzen wir die Größe der dynamischen Arrays um 1 größer als die Tickzahl ArrayResize(X2,ticks+1); ArrayResize(X,ticks+1); //--- sichern wir uns Nullwerte am Anfang der Berechnungen ZeroMemory(X2); ZeroMemory(X); firstcall=false; } //--- Arrays verschieben ArrayCopy(X,X,1,0,ticks); ArrayCopy(X2,X2,1,0,ticks); //--- neue eingehende Summenwerte berechnen X[0]=DeltaTickBuffer[0]; X2[0]=DeltaTickBuffer[0]*DeltaTickBuffer[0]; //--- berechnen wir neue Summen Xsum=Xsum+X[0]-X[ticks]; X2sum=X2sum+X2[0]-X2[ticks]; //--- Quadratzahl der Standardabweichung double S2=(1.0/(ticks-1))*(X2sum-Xsum*Xsum/ticks); //--- berechnen wir die Summen von Ticks und die mathematische Erwartung double stddev=MathSqrt(S2); //--- return (stddev); }
Fügen wir die Berechnung der Standardabweichung auf die zweite Weise über die Funktion getStdDevOptimized() in die Funktion ApplyTick() hinzu und starten wir das Profiling erneut.
//--- berechnen wir die Differenz mit dem vorherigen Wert DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1]; //--- wir bekommen die Standardabweichung double stddev=getStdDev(ticks); double std_opt=getStdDevOptimized(ticks);
Das Ergebnis:
Man sieht, dass die neue Funktion getStdDevOptimized() im Vergleich zur direkten Berechnung in der getStdDev() (15,50%) halb so viel Zeit — 7,12% — braucht. Es ist auch empfehlenswert, den Artikel 3 Methoden zur Beschleunigung von Indikatoren anhand des Beispiels der linearen Regression zu lesen.
Apropos Aufruf von Standardfunktionen: in diesem Indikator bekommen wir den Preis aus der Zeitserie close[], die anhand Bid-Preise gebildet wird. Es bestehen noch zwei Möglichkeiten diesen Preis zu bekommen: mithilfe der Funktionen SymbolInfoDouble() und SymbolInfoTick(). Fügen wir dem Code diese Aufrufe hinzu und starten wir das Profiling erneut.
Wie Sie sehen können, gibt es hier auch Unterschiede hinsichtlich der Geschwindigkeit. Das ist auch verständlich, denn das Lesen eines fertigen Preises von close[] ist nicht so aufwendig im Vergleich zum Aufruf universeller Funktionen.
Debugging mit echten Ticks im Tester
Beim Erstellen von Indikatoren und Handelsrobotern kann man nicht alles vorhersehen, was alles beim Online-Betrieb passieren kann. Zum Glück erlaubt MetaEditor das Debbugen auch anhand historischer Daten. Starten Sie einfach Debugging im visuellen Modus und Sie können Ihr Programm im angegebenen Zeitraum der Historie testen. Sie können das Testen beschleunigen, stoppen und zum gewünschten Datum scrollen.
Wichtig: geben Sie den Modellierungsmodus "Jeder Tick anhand realer Ticks" im Debugging-Fenster aus. Das ermöglicht es, echte Kurse für das Debugging zu verwenden, die auf dem Handelsserver gespeichert sind. Beim ersten Start des Tests werden diese automatisch auf Ihr PC geladen.
Wenn diese Parameter nicht in MetaEditor gesetzt wurden, werden im visuellen Modus die aktuellen Einstellungen des Testers genutzt. Wählen Sie den Modus "Jeder Tick anhand realer Ticks" aus.
Wir sehen seltsame Lücken auf dem Tickchart. Das heißt, es gibt einen Fehler im Algorithmus. Es ist unbekannt, wie lange man gebraucht hätte, bis dieser Fehler beim Testen in der Echtzeit zum Vorschein gekommen wäre. In diesem Fall sieht man im Journal des visuellen Testens, dass die komischen Lücken beim Erscheinen eines neuen Balkens auftreten. Genau! Wir haben vergessen, dass die Größe der Indikatorpuffer beim Übergang zu einem neuen Balken automatisch um 1 erhöht wird. Korrigieren wir den Code:
void ApplyTick(double price) { //--- wir werden nun die Größe des Arrays TickPriceBuffer speichern - er ist gleich der Balkenzahl im Chart static int prev_size=0; int size=ArraySize(TickPriceBuffer); //--- wenn sich die Größe der Indikatorpuffer nicht geändert hat, verschieben wir die Elemente um eine Position zurück if(size==prev_size) { //--- wie viele Elemente auf jedem Tick in den Indiaktorpuffern verschoben werden int move=ArraySize(TickPriceBuffer)-1; if(shift!=0) move=shift; ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move); ArrayCopy(SignalBuffer,SignalBuffer,1,0,move); ArrayCopy(SignalColors,SignalColors,1,0,move); ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move); } prev_size=size; //--- speichern wir den letzten Wert TickPriceBuffer[0]=price; //--- berechnen wir die Differenz mit dem vorherigen Wert
Starten wir visuelles Testen und setzen wir Haltepunkte, um den Moment der Eröffnung eines neuen Balkens zu erwischen. Fügen wir die zu beobachtenden Werte hinzu: die Anzahl der Balken ist um 1 größer geworden, das Tick-Volumen beträgt 1 - das ist der erste Tick des neuen Balkens.
Wir haben den Code optimiert, Fehler behoben und die Ausführungszeit verschiedener Funktionen gemessen, nun ist der Indikator anwendungsbereit. Man kann visuelles Testen starten und beobachten, was nach dem Eintreffen von Signalen auf dem Tick-Chart erscheint. Könnte man den Code des Indikators noch verbessern? Perfektionisten würden "Ja!" sagen. Wir haben noch nicht versucht, den Ringpuffer für die Beschleunigung zu verwenden. Wenn Sie Lust haben, können Sie selbst prüfen, ob das die Performance des Indikators verbessert.
MetaEditor — ein Labor für die Entwicklung von Handelsstrategien
Für die Erstellung eines automatischen Trading-Systems werden nicht nur eine gute Entwicklungsumgebung und eine leistungsstarke Programmiersprache benötigt, sondern auch zusätzliche Tools für Debugging und Profiling. In diesem Artikel haben wir gezeigt, wie:
- ein Tickchart innerhalb von wenigen Minuten erstellt werden kann;
- man ein Programm in der Echtzeit mithilfe der F5-Taste debuggen kann;
- man Profiling startet, um ineffiziente Stellen im Code festzustellen;
- man einen Code anhand historischer Daten im visuellen Modus schnell debuggen kann;
- man Variablenwerte im Laufe des Debuggens ansehen kann.
Die Entwicklung eines Indikators, der Handelssignale anzeigt, ist häufig der erste Schritt zur Erstellung eines Handelsroboters. Die Visualisierung hilft Handelsregeln zu erarbeiten oder eine Idee sofort abzulehnen.
Nutzen Sie alle Möglichkeiten der Entwicklungsumgebung MetaEditor für die Erstellung effektiver Handelsroboter!
Artikel zum Thema:
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2661
- 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.
Der bisher beste Artikel den ich hier gefunden habe !
Ich beschäftige mich seit einiger Zeit mit diesen Tickdaten (Tickcharts), denn das sind die realen Daten.
Alle Darstellungen in Kerzencharts sind IMHO irreführend.
Die Handhabung der Arrays ist hier besser(schneller) als mit ShiftBuffers(...) zu arbeiten.
Die gleitenden Durchschnitte, und alle anderen Indikatoren, muss man sich allerdings selber proggen.
Den SMA und LWMA habe ich dafür.
Muss ich aber noch abgleichen, da ich OOP-Indikatorbuffer verwende, die ein bisschen intelligenter sind.
Die kann man dann fragen ob sie steigen oder fallen, oder einen Wendepunkt passiert haben,
sich gekreuzt haben, oder wie stark sie fallen oder steigen.
Sobald ich's fertig habe (wird wohl 14 Tage dauern, hab grad viel zu tun (Pensionistengruß: 'Hab ka Zeit')) poste ich den quellcode für MT5 hier gerne.
Ich hab ja was zu verschenken ;-)
Grüße vom OPA