English Русский 中文 Español 日本語 Português
Wie man eine Handelsstrategie in MetaTrader 5 schnell entwickeln und debuggen kann

Wie man eine Handelsstrategie in MetaTrader 5 schnell entwickeln und debuggen kann

MetaTrader 5Beispiele | 12 September 2016, 10:09
2 639 1
MetaQuotes
MetaQuotes

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:

  1. ein Tickchart innerhalb von wenigen Minuten erstellt werden kann;
  2. man ein Programm in der Echtzeit mithilfe der F5-Taste debuggen kann;
  3. man Profiling startet, um ineffiziente Stellen im Code festzustellen;
  4. man einen Code anhand historischer Daten im visuellen Modus schnell debuggen kann;
  5. 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:

  1. MQL5: Erstellen Ihres eigenen Indikators
  2. Erzeugung von Tick-Indikatoren in MQL5
  3. Prinzipien der wirtschaftlichen Berechnung von Indikatoren
  4. Bildung von Kursreihenmittelwerten für Zwischenberechnungen ohne zusätzliche Puffer
  5. Zur Fehlerbehebung von MQL5-Programmen (Debugging)

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

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Otto Pauser
Otto Pauser | 12 Sept. 2016 in 23:08

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

Graphische Interfaces VIII: Der Kalender (Kapitel 1) Graphische Interfaces VIII: Der Kalender (Kapitel 1)
Im Kapitel VIII der Reihe von Artikeln, die sich der Erstellung graphischer Oberflächen im MetaTrader widmet, betrachten wir komplexe, zusammengesetzte Steuerelemente wie Kalender, Baumdarstellung und einen Dateinavigator. Aufgrund der umfangreichen Informationen gibt es für jedes Thema eigene Artikel. Das erste Kapitel dieses Teil beschreibt das Kalenderelement und seine erweiterte Version, ein Dropdown-Kalender.
Grafische Interfaces VII: Das Tab-Control (Kapitel 2) Grafische Interfaces VII: Das Tab-Control (Kapitel 2)
Das erste Kapitel des siebten Teils befasste sich mit drei Klassen von Controls für die Erzeugung der folgenden Tabellen: Text-Label-Tabelle (CLabelsTable), Edit-box-Tabelle (CTable) und die gerenderte Tabelle (CCanvasTable). In diesem Artikel(Kapitel 2) werden wir das Tabs-Control besprechen.
Grafische Interfaces VIII: Die Baumansicht (Kapitel 2) Grafische Interfaces VIII: Die Baumansicht (Kapitel 2)
Das vorherige Kapitel VIII der grafischen Schnittstellen hat sich mit den Elementen eines statischen und eines Dropdown-Kalenders beschäftigt. Das zweite Kapitel beschäftigt sich mit einem nicht weniger komplexen Element — der Baumansicht, die Teil aller kompletten Bibliotheken graphischer Schnittstellen ist. Die Baumansicht in diesem Artikel beinhaltet mehrere flexible Einstellungen und Modi und erlaubt daher die Elemente ganz Ihren Zielen anzupassen.
Cross-Plattform Expert Advisor: Wiederverwendung von Komponenten aus der Standardbibliothek von MQL5 Cross-Plattform Expert Advisor: Wiederverwendung von Komponenten aus der Standardbibliothek von MQL5
Es gibt einige Komponenten in der Standardbibliothek von MQL5, die für eine MQL4-Version eines Cross-Plattform Expert Advisors hilfreich sein könnten. Dieser Artikel befasst sich mit einem Verfahren zur Herstellung von bestimmten Komponenten aus der Standardbibliothek von MQL5, das sie kompatibel für den Kompiler der Programmiersprache MQL4 macht.