Die Übertragung eines Indikatorcodes in einen Expertcode. Die Struktur eines Indikators

Nikolay Kositsin | 14 April, 2016

Einleitung

Damit Sie den Beitrag besser verstehen könnten, empfiehlt der Autor dem Leser das folgende Material:

  1. MetaQuotes Software Corp. Besonderheiten benutzerdefinierter Indikatoren. https://www.mql5.com/de/articles/1497
  2. Nikolay Kositsin. Mehrmalige Neuberechnungen des nullwertigen Bars in einigen Indikatoren. https://www.mql5.com/de/articles/1411

Bevor wir das Gespräch über das Thema anfangen, welches ich im Titel angedeutet habe, wäre die folgende Frage zur Recht: "Wieso brauchen wir einen Indikatorcode in einen EA-Code zu übertragen, wenn der Expert mit benutzerdefinierten Indikatoren in den meisten Fällen viel einfacher aussieht, als seine Analoge, die alle notwendigen benutzerdefinierten Indikatoren für seine Funktionalität innerhalb des eigenen Codes enthält? Vor allem, wenn wir die Tatsache berücksichtigen, dass die Ergebnisse in beiden Fällen absolut identisch sein werden, wenn der Code korrekt geschrieben wird!"

Meiner Meinung nach ist es nur in zwei Situationen notwendig:

  1. Wenn die Werte bei EA-Berechnungen nicht verwendet werden, die auf dem nullwertigen Bar berechnet wurden, dann würden wir natürlich gerne auf die unnötigen Neuberechnungen auf dem nullwertigen Bar verzichten und genauso auf dem ersten Bar. Dies ermöglicht die Optimierungszeit eines solchen EAs dreimal zu verkürzen, was bei einem komplizierten und ressource-intensiven Code sehr aktuell ist!
  2. Die kommerzielle Verwendung eines Expert Advisors mit dem maximalen Schutz seines Codes gegen seine Decompiling.


Im zweiten Fall ist die Situation ganz klar, und die Übertragung des Codes ist durchaus sinnvoll. Während es im ersten Fall ist in den meisten Situationen viel einfacher, den Code der benutzerdefinierten Indikatoren neu zu schreiben, ohne unnötige Berechnungen! Natürlich werden solche Indikatoren nur für Expert Advisors geeignet, und nicht für das Handeln! Also, lassen Sie uns bitte den Artikel von dieser Variante der Problemlösung beginnen.


Beispiele für Indikator-Optimierung

Zunächst einmal würde ich Ihre Aufmerksamkeit auf das folgende Codefragment eines benutzerdefinierten Indikators lenken:

int start()
  {
    int limit;
    int counted_bars = IndicatorCounted();
//---- das letzte berechnete Bar wird neuberechnet
    if(counted_bars > 0) 
        counted_bars--;
    limit = Bars - counted_bars - 1;
//---- Die Hauptloop
    for(int i = limit; i >= 0; i--)
      {
        //---- 
        ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
        ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, 
                              PRICE_MEDIAN, i);
        ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
      }
//----
     return(0);
  }

In diesem Fall würde uns die folgende Zeile interessieren:

if(counted_bars > 0) 
    counted_bars--;

Die Bedeutung dieser Prüfung mit Verringerung des Wertes der Variable counted_bars um eins ist die folgende: Wenn ein benutzerdefinierter Indikator diese Zeile nicht enthält, kann er falsche Werte aus seinem Puffer in EA senden, wenn das nullwertige Bar geändert wird. Die Indikatorkurve im EA wird ganz "zerknittert" aussehen!

In benutzerdefinierten Indikatoren können die Variablen limit und counted_bars verschiedene Namen haben, aber der Programmcode muss diese Überprüfungen haben! Ich nehme an, dass diese Erklärung genug ist, um Behauptungen einiger so genannten EA-Autoren zu erklären, dass im MetaTrader die Daten des Indikatorpuffers und die gleichen Daten, die aus einem benutzerdefinierten Indikator empfangen wurden, sind nicht identisch! Wenn ein Indikatorcode und ein EA-Code korrekt geschrieben werden, egal wie schwierig der Indikatorcode ist, werden die Daten immer gleich sein!

Aber es sollte hier angemerkt werden, dass einige Glättungsalgorithmen zum Referenzpunkt empfindlich sind, aus dem die Glättung beginnt. Das heißt, um identische Werte zu haben, muss man die Übereinstimmung der ältesten Bars-Nummer in Loops besorgen, sowie in Indikatoren, als auch im Indikatorcode innerhalb des EAs, von dem die Neuberechnung aller Bars beginnt.

Hier ist ein Beispiel, welches diese Methode einer Indikatorcodeoptimierung für die schnellere Funktionalität im EA erklärt. Im Hauptloop des Indikators ersetzen wir 0 um 1, nachdem der Indikator die Neuberechnung seines Wertes auf dem nullwertigen Bar aufhört.

// Anstatt von
for(int i = limit; i >= 0; i--)      
// einfügen
for(int i = limit; i >= 1; i--)

Als Ergebnis wird der Quellcode wie folgende aussehen:

int start()
  {
    int limit;
    int counted_bars = IndicatorCounted();
//---- das letzte berechnete Bar wird neuberechnet
    if(counted_bars > 0) 
        counted_bars--;
    limit = Bars - counted_bars - 1;
      
  //---- Die Hauptloop //nun beendet die Loop auf 1
    for(int i = limit; i >= 1; i--)
      {
        //---- 
        ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
        ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, 
                              PRICE_MEDIAN, i);
        ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, 
                               PRICE_MEDIAN, i);
      }
  //----
    return(0);
  }
Ich wiederhole die Tatsache, dass das obige Verfahren für EAs ist, die nur auf geschlossenen Bars funktionieren, das heißt, an allen Bars außer den nullwertigen Bars!

Es wird angenommen, dass der Leser die Optimierung des Indikatorencodes gut kennt, damit dieser Indikator nur auf den Ticks der ungerechneten Bars neuberechnet. Meiner Meinung nach sollte man das Programmieren in MQL4 erstmal von diesen Aspekten lernen.

Wenn Sie Ihren EA ernsthaft im realen Handeln verwenden wollen und Ihr Geld vertrauen, dann sollten Sie in Ihrem EA sorgfältig alle Details überprüfen, sowie die Indikatoren, mit denen der EA arbeitet. Außerdem sollten Sie es selbst tun!! Ich denke, es ist viel einfacher und vernünftiger ein paar Tage zu lernen, um ein gründliches Verständnis von der Struktur des Indikators und Methoden der Optimierung ihres Codes zu bekommen, als mit Geduld drei Monate lang an dem optimierten EA sitzen zu bleiben, der die Werte aus einem grob geschrieben Indikator erhält!

Also ich hoffe, es muss jetzt klar sein, dass man ohne schwerwiegende Gründe den Indikatorcode in einen Expertcode nicht übertragen soll. Wenn ein Indikator korrekt und optimal geschrieben ist, wird der EA ohnehin nicht viel langsamer funktionieren. Das ist generell viel einfacher zuerst einen EA-Code mit der Verwendung der benutzerdefinierten Indikatoren zu schreiben und die Einrichtungsarbeiten in dieser Form durchzuführen. Und wenn Ihr EA nach allen diesen wirklich perfekte Ergebnisse zeigt, kann der Code erst dann weiter optimiert werden, einen Aufruf zu benutzerdefinierten Indikatoren nach dem anderen um Codefragmente der Indikatoren zu ersetzen.

Dabei muss man die Tatsache berücksichtigen, dass die getesteten Gewinnwerte und Verlustwerte nach der Ersetzung des EA-Codes nicht anderes werden sollten! Aus meiner Sicht hat es einen Sinn, nur einfache Situationen mit Übertragung zu betrachten, wenn der Indikator angemessene Größe des Codes hat. Ansonsten wird die Aufgabe so schwer, dass die Lösung dieser Aufgabe die Erkenntnisse eines hochqualifizierten Programmierers verlangt, der das sowieso gut kennt!

Es gibt jede Menge Indikatoren, und jeder hat seinen einzigartigen Code. Deshalb kann man kaum eine universelle Methode für eine Übertragung eines Codes schaffen, die ohne Ausnahme zu allen Indikatoren passen würde. Das Problem wird durch die Tatsache komplizierte, dass dasselbe benutzerdefinierte Indikatoren in einem EA-Code mehrmals vorkommen können! Wenn ein Indikatorcode mehr oder weniger einfach ist, kann er in einer benutzerdefinierte Funktion geschrieben werden und dann wird die Ersetzung der benutzerdefinierten Indikatoren zu Funktionen ganz einfach gehen. Aber sehr oft wird der EA-Code zu groß, dass es fast unmöglich ist, einen Fehler darin zu finden. Und alle unsere Bemühungen werden dann vergeblich!


Ein allgemeines Schema der Struktur eines Indikators


Bevor wir zum Hauptthema des Artikels gehen, lassen Sie uns zunächst eine allgemeine Struktur eines Indikators aus der Sicht eines Programmierers analysieren, der sich für diesen Indikator als ein zukünftiges Teil eines EA-Codes interessiert:
//+------------------------------------------------------------------+
//|                                                IndicatorPlan.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- Darstellung des Indikators im Hauptfenster
#property indicator_chart_window 
//---- die Anzahl der Indikatorpuffers
#property indicator_buffers 1 
//---- die Farbe des Indikators
#property indicator_color1 Gold
//---- Eingabeparameter des Indikators
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- Indikatorpuffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- Definieren des Zeichenstils vom Chart 
   SetIndexStyle(0, DRAW_LINE); 
//---- 3 Indikatorpuffers wurden für die Zählung verwendet
   IndicatorBuffers(3);
   SetIndexBuffer(0, Ind_Buffer0); 
   SetIndexBuffer(1, Ind_Buffer1);
   SetIndexBuffer(2, Ind_Buffer2);
//---- Das Ende des Initialisierens 
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Die Überprüfung der Anzahl der Bars, damit sie ausreichend für die Berechnung wären
   if(Bars < period0 + period1 + period2)
       return(0);
//----+ Eingabe der Variablen mit Gleitkomma
   double Resalt0, Resalt1, Resalt2;
//----+ Eingabe der ganzen Variablen und erhalten dadurch bereits berechneten Bars
   int limit, MaxBar,bar, counted_bars = IndicatorCounted();
//---- Überprüfung auf mögliche Fehler
   if(counted_bars < 0)
       return(-1);
//---- das letzte berechnete Bar muss neuberechnet werden 
   if(counted_bars > 0) 
       counted_bars--;
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
// der neuen Bars beginnt
   limit = Bars - counted_bars - 1; 
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
//  aller Bars beginnt
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- Initialisieren der Null 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= MaxBar; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ Die erste Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt1 auf der Grundlage der externen 
       //Variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ Die zweite Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt2 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer1[] und 
       // der externen Variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ Die Hauptloop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt0 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer2[] und 
       // der externen Variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+
Natürlich kann ein echter Indikator eine andere Anzahl von reflektierten Indikatorwerten haben, andere Anzahl von Indikatorpuffern, die bei Berechnungen verwendet werden und andere Anzahl von Loops für die Berechnung der Werte von Indikatorpuffern, aber das ändert nicht die Bedeutung des angegebenen Schemas. In anderen Fällen wird es absolut gleich sein.

Nun lassen uns allen unnötigen Elemente aus diesem Schema löschen, die uns in diesem Zusammenhang nicht interessieren, und die im Expert Advisor nicht notwendig sind:

//+------------------------------------------------------------------+
//|                                               IndicatorPlan1.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
//---- Eingabeparameter des Indikators
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- Indikatorpuffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- Das Ende des Initialisierens 
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Die Überprüfung der Anzahl der Bars, damit sie ausreichend für die Berechnung wären
   if(Bars < period0 + period1 + period2)
       return(0);
//----+ Eingabe der Variablen mit Gleitkomma
   double Resalt0, Resalt1, Resalt2;
//----+ Eingabe der ganzen Variablen und erhalten dadurch bereits berechneten Bars
   int limit, MaxBar, bar, counted_bars = IndicatorCounted();
//---- Überprüfung auf mögliche Fehler
   if(counted_bars < 0)
       return(-1);
//---- das letzte berechnete Bar muss neuberechnet werden 
   if(counted_bars > 0) 
       counted_bars--;
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
// der neuen Bars beginnt
   limit = Bars - counted_bars - 1; 
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
//  aller Bars beginnt
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- Initialisieren der Null 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= MaxBar; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ Die erste Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt1 auf 
       // der Grundlage der externen Variable period1
       Ind_Buffer1[bar]= Resalt1;
     }
//----+ Die zweite Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt2 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer1[] und 
       // der externen Variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ Die Hauptloop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt0 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer2[] und 
       // der externen Variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+
Dieser Code könnte allerdings leicht in einem Expert Advisor-Code platziert werden, wenn es nicht ein paar kleine Versehen gäben
  1. Vor allem sollten wir nicht vergessen, dass die Funktion IndicatorCounted() in Expert Advisors nicht funktioniert!
  2. Wir können auch nicht von den benutzerdefinierten Arrays im Initialisierungsblock um die Indikators Arrays ändern!

Also für eine vollständige Speicherung des Indikatorcodes brauchen wir vor allem ein Analog der Funktion IndicatorCounted() zu erstellen und irgendwie die Analoge der Indikatorpuffer in einem Expert Advisor zu emulieren. Leider ist es nicht möglich, unter Verwendung der Standardfunktionen direkt due Indikatorpuffer in einem EA zu emulieren. Inzwischen gibt es keine Analoge von SetIndexBuffer() und IndicatorBuffers() für Expert Advisors! Also muss dieses Problem anderes gelöst werden. Zum Glück gibt es dafür genug Möglichkeiten in MQL4.


Emulation der Indikatorpuffer in einem Expert Advisor

Zunächst lassen Sie uns in Details die Prozesse betrachten, die in Indikatorpuffern laufen.

  1. Die Werte, die der Variablen der Indikatorpuffer zugeordnet werden, werden nicht zwischen den Loops verloren, solange der Indikator zum Chart hinzugefügt bleibt und solange der Terminal arbeitet.
  2. Wenn das nullwertige Bar (das neueste) auf dem Chart geändert wird, werden alle Elemente des Indikatorpuffers verschoben.
  3. Wenn ein neues Bar kommt, wird sich der Wert der Variable limit von 1 um 2 im Indikator ändern!, und alle Pufferelemente durch 1 verschoben, das heißt, das nullwertiges Element wird dem ersten, das erste wird dem zweiten und so weiter.
  4. Natürlich werden sich die Abmessungen der Indikatorpuffer ändern. Wenn auf dem nächsten Tick hat sich das nullwertige Bar nicht geändert, werden alle Pufferelemente auf ihren Plätzen bleiben.

Jetzt kommt die Emulation der Indikatorpuffer. Zu diesem Zweck werden wir die folgenden MQL4 Standardfunktionen verwenden: ArraySize(), ArrayResize() und ArraySetAsSeries(). Der Emulationscode der Indikatorpuffer ist ganz einfach und das Prinzip seiner Operation kann man so beschrieben: Wenn das nullwertige Bar sich ändert, wird die direkte Reihenfolge der Elementen-Definition in Puffern wiederhergestellt, es werden durch die Funktion ArrayResize() neue Einheiten in den Puffern von neuen Bars hinzugefügt, danach wird die umgekehrte Reihenfolge der Elemente-Definition in Puffern gesetzt und leere Einheiten erscheinen unter dem ersten emulierten Indikatorpuffer sein.

//---- Die Emulation der Indikatorpuffer
  int NewSize = iBars(symbol, timeframe);
  //----   Überprüfung der Änderung des nullwertigen Bars
  if(ArraySize(Ind_Buffer0) < NewSize)
    {
      //---- Stellen Sie die direkte Richtung des Indexeds in Arrays ein 
      ArraySetAsSeries(Ind_Buffer0, false);
      ArraySetAsSeries(Ind_Buffer1, false);
      ArraySetAsSeries(Ind_Buffer2, false);
      //---- Ändern Sie die Größe der emulierten Indikatorpuffer 
      ArrayResize(Ind_Buffer0, NewSize); 
      ArrayResize(Ind_Buffer1, NewSize); 
      ArrayResize(Ind_Buffer2, NewSize); 
      //---- Stellen Sie die Rückwärtsrichtung des Indexed in Arrays ein 
      ArraySetAsSeries(Ind_Buffer0, true);
      ArraySetAsSeries(Ind_Buffer1, true);
      ArraySetAsSeries(Ind_Buffer2, true); 
    } 
//----

Übrigens kann diese Emulationsmethode der Indikatorpuffer auch in Indikatoren verwendet werden, wenn acht Indikatorpuffer für die Zwischenberechnungen eines Indikators nicht ausreichend sind. Die Beispiele dafür sind in den beigefügten Dateien SMI.mq4 und SMI_New.mq4.



Die Ersetzung der Funktion IndicatorCounted().

Lassen Sie uns nun die Emulation der Funktion IndicatorCounted() analysieren. Diese Funktion liefert die Anzahl des aktuellen Chart-Bars, die nach dem letzten Aufruf des Indikators unverändert geblieben sind. Oder wir können es mit anderen Worten so sagen. Diese Funktion liefert die Anzahl des aktuellen Chart-Bars, die im Client-Terminal auf dem vorherigen Tick zur Verfügung standen! Damit die Werte absolut identisch von der Anzahl der resultierenden Bars wären, müssen wir eins daraus subtrahieren. So können wir diese Funktion einfach durch eine statische ganzzahlige Variable ersetzten, die durch den Wert einer vorbestimmten Variable Bars-1 initialisiert wird, nachdem ein Wert von ihr empfangen wird. Danach wird das Schema des Indikators so aussehen:

//+------------------------------------------------------------------+
//|                                            NewIndicatorPlan1.mqh |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
 
//---- Eingabeparameter des Indikators
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- Indikatorpuffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- Das Ende des Initialisierens 
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//---- Die Überprüfung der Anzahl der Bars, damit sie ausreichend für die Berechnung wären
   if(Bars < period0 + period1 + period2)
       return(0);
//---- Die Emulation der Indikatorpuffer
   if(ArraySize(Ind_Buffer0) < Bars)
     {
       ArraySetAsSeries(Ind_Buffer0, false);
       ArraySetAsSeries(Ind_Buffer1, false);
       ArraySetAsSeries(Ind_Buffer2, false);
       //----  
       ArrayResize(Ind_Buffer0, Bars); 
       ArrayResize(Ind_Buffer1, Bars); 
       ArrayResize(Ind_Buffer2, Bars); 
       //----
       ArraySetAsSeries(Ind_Buffer0, true);
       ArraySetAsSeries(Ind_Buffer1, true);
       ArraySetAsSeries(Ind_Buffer2, true); 
     } 
//----+ Eingabe der ganzen statistischen Speichervariable
   static int IndCounted;
//----+ Eingabe der Variablen mit Gleitkomma
   double Resalt0, Resalt1, Resalt2;
//----+ Eingabe der ganzen Variablen und erhalten dadurch bereits berechneten Bars
   int limit, MaxBar, bar, counted_bars = IndCounted;
//---- Überprüfung auf mögliche Fehler
   if(counted_bars < 0)
       return(-1);
//---- das letzte berechnete Bar muss neuberechnet werden 
   if(counted_bars > 0) 
       counted_bars--;
//----+ Die Speicherung der Anzahl aller Chart-Bars
   IndCounted = Bars - 1;
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
//     der neuen Bars beginnt
   limit = Bars - counted_bars - 1; 
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
//     aller Bars beginnt
   MaxBar = Bars - 1 - (period0 + period1 + period2); 
//---- Initialisieren der Null 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = Bars - 1; bar >= 0; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ Die erste Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt1 auf der Grundlage der 
       // externen Variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ Die zweite Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt2 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer1[] und der externen Variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ Die Hauptloop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt0 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer2[] und der externen Variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Die weitere Transformation des Indikatorcodes und das Schema seiner Struktur

Natürlich könnten wir den Indikatorcode einfach in Teilen in einen EA-Code übertragen, wenn wir annehmen, dass wir diesen Indikator im Expert Advisor für die Arbeit auf dem aktuellen Chart nur einmal brauchen! Wenn der Indikator zweimal verwendet wird, dann könnten wir einfach im zweiten Fall die Namen aller Indikatorvariablen ändern und den Code noch mal hinzufügen. Aber in diesem Fall wird der Expert Advisor komplizierter sein.

Die Aufgabe der Verarbeitung von Daten aus anderen Timeframes wird auch leicht gelöst. Ersetzen wir im Indikator die vorbestimmten Variablen Bars um die Zeitreihenanordnungen
iBars(string symbol,  int timeframe);

NULL - на string symbol;, 0(in Zeitreihenanordnungen) - um int timeframe;, Close[bar] - на

iClose(string symbol, int timeframe, bar);

und so weiter.

Lassen Sie uns nun die Zeilen des Indikators analysieren:

//---- Überprüfung auf mögliche Fehler
if(counted_bars < 0)
    return(-1);
//---- das letzte berechnete Bar muss neuberechnet werden 
if(counted_bars > 0) 
    counted_bars--;

Die angebotene Ersetzung der Funktion IndicatorCounted() der Indikatorstruktur der Variable counted_bars wird in unserer Variante nie kleiner als Null sein. Also die Zeilen:

//---- Überprüfung auf mögliche Fehler
if(counted_bars < 0)
    return(-1);

im EA-Code kann man löschen. Mit den nächsten beiden Zeilen:

//---- das letzte berechnete Bar muss neuberechnet werden 
if(counted_bars > 0) 
    counted_bars--;

Es ist das Gleiche. Sie sollten gelöscht werden, denn sie verlangsamen nur die Arbeit des Expertes durch die unnötigen Neuberechnungen des ersten Bars und diese Prüfung ist im EA-Code absolut nutzlos. Danach wird der endgültige Code im EA für die Übertragung so aussehen:

//+------------------------------------------------------------------+
//|                                            NewIndicatorPlan2.mqh |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
 
//---- Eingabeparameter des Indikators
extern int period0 = 15;
extern int period1 = 15;
extern int period2 = 15;
//---- Indikatorpuffers
double Ind_Buffer0[];
double Ind_Buffer1[];
double Ind_Buffer2[];
//---- Veröffentlichen der Variablen für die Auswahl des Charts
string symbol; int timeframe;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- Die Auswahl des Charts für die Berechnung des Indikators
   symbol = Symbol();//Initialisieren der Variable symbol;
   timeframe =240;//Initialisieren der Variable timeframe;
//---- Das Ende des Initialisierens 
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
// Die Erhaltung der Anzahl aller Chart-Bars
   int IBARS = iBars(symbol, timeframe);
//---- Die Überprüfung der Anzahl der Bars, damit sie ausreichend für die Berechnung wären
   if(IBARS < period0 + period1 + period2)
       return(0);
// Die Emulation der Indikatorpuffer
   if(ArraySize(Ind_Buffer0) < IBARS)
     {
       ArraySetAsSeries(Ind_Buffer0, false);
       ArraySetAsSeries(Ind_Buffer1, false);
       ArraySetAsSeries(Ind_Buffer2, false);
       //----  
       ArrayResize(Ind_Buffer0, IBARS); 
       ArrayResize(Ind_Buffer1, IBARS); 
       ArrayResize(Ind_Buffer2, IBARS); 
       //----
       ArraySetAsSeries(Ind_Buffer0, true);
       ArraySetAsSeries(Ind_Buffer1, true);
       ArraySetAsSeries(Ind_Buffer2, true); 
     } 
// Eingabe der ganzen statistischen Speichervariable
   static int IndCounted;
//----+ Eingabe der Variablen mit Gleitkomma
   double Resalt0, Resalt1, Resalt2;
//----+ Eingabe der ganzen Variablen und erhalten dadurch bereits berechneten Bars
   int limit, MaxBar, bar, counted_bars = IndCounted;
//----+ Die Speicherung der Anzahl aller Chart-Bars
   IndCounted = IBARS - 1;
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung,   
//     der neuen Bars beginnt
   limit = IBARS - counted_bars - 1; 
//---- Die Bestimmung der Nummer des letzten Bars, von dem die Neuberechnung, 
//     aller Bars beginnt
   MaxBar = IBARS - 1 - (period0 + period1 + period2); 
//---- Initialisieren der Null 
   if(limit > MaxBar)
     {
       limit = MaxBar;
       for(bar = IBARS - 1; bar >= 0; bar--)
         {
           Ind_Buffer0[bar] = 0.0;
           Ind_Buffer1[bar] = 0.0;
           Ind_Buffer2[bar] = 0.0;
         }
     }
//----+ Die erste Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt1 auf der Grundlage der externen 
       //Variable period1
       Ind_Buffer1[bar] = Resalt1;
     }
//----+ Die zweite Loop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt2 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer1[] und der externen Variable period2
       Ind_Buffer2[bar] = Resalt2;
     }
//----+ Die Hauptloop der Berechnung des Indikators 
   for(bar = limit; bar >= 0; bar--)
     {
       // Hier ist der Berechnungscode der Variable Resalt0 
       // auf der Grundlage des Wertes vom Puffer Ind_Buffer2[] und der externen Variable period0
       Ind_Buffer0[bar] = Resalt0;
     }
   return(0);
  }
//+------------------------------------------------------------------+
Hier sollten wir einen Moment berücksichtigen. Es gibt Indikatoren, die bei der mehrmaligen Neuberechnung des nullwertigen Bars die Werte einiger Variablen speichern, welche auf dem ersten Bar am Anfang der Berechnungsloops sind, um den Code in den Ausgangszustand zu liefern (Artikel). Im Expert Advisor sollte es nach der Löschung der letzten 2 Zeilen nicht auf dem ersten Bar gespeichert werden, sondern auf dem nullwertigen Bar. Normalerweise enthalten solche Indikatoren am Anfang der Berechnungsloops des Indikators die folgenden Code-Fragmente:
// Speicherung des Wertes der Variablen
if(bar == 1)
    if(((limit == 1) && (time == Time[2])) || (limit > 1))
      {
        time = Time[2];
        // Diese Variablen interessieren uns nicht
        PRICE = price;
        TREND = trend;
        RESALT = Resalt;
      }
//+------------------------------------------------------------------+
Dieses Fragment sollte zum Speichern der Variablenwerte nicht auf dem ersten, sondern auf nullwertigen Bar geändert werden. Alle 1 sollten im Code um 0 ersetzt werden, und 2 um 1. Und wenn der Code nicht auf dem aktuellem Chart verwendet wird, sondern auf irgendwelchem anderen, dann sollte der Aufruf zum Array durch die Zeitreihenanordnung ersetzt werden
time = Time[1];

durch

time = iTime(symbol, timeframe, 1);

Als Ergebnis haben wir das folgende geänderte Fragment:

// Speicherung des Wertes der Variablen
if(bar == 0)
    if(((limit == 0) && (time == iTime(symbol, timeframe, 1))) || (limit > 0))
      {
        time = iTime(symbol, timeframe, 1);
        PRICE = price;
        TREND = trend;
        RESALT = Resalt;
      }
//+------------------------------------------------------------------+

Im Indikatorcode können auch von mir entwickelten Funktionen der Glättung wie XXXSeries() sein. Um die Fragmente mit solchen Funktionen im EA-Code zu verwenden, sollten diese Funktionen durch die EA-Analoge ersetzt werden.


Fazit

In diesem Moment ist es zweifellos, dass der angebotene Algorithmus einen Indikator-Code in einen EA-Code in dieser Form zu übertragen, ist eher umständlich und nicht optimal, aber der Zweck dieses Artikels ist nicht eine genaue Beschreibung aller Einzelheiten dieses Prozesses. Die Hauptidee dieses Artikels ist eine allgemeine Vorstellung von Indikatoren zu geben und die Analyse der allgemeinen Idee, den Code in einer maximal einfachen Form zu übertragen, und das nicht mit sekundären Details kompliziert zu machen. Ich finde, dass wir dieses Ziel erreicht haben! Im nächsten Artikel werden wir die allgemeine Struktur eines Expert Advisors und Strukturschemas der Indikatorfunktionen analysieren.