English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Nutzung von Pseudo-Templates als Alternative für C++-Templates

Nutzung von Pseudo-Templates als Alternative für C++-Templates

MetaTrader 5Beispiele | 7 April 2016, 10:47
536 0
Mykola Demko
Mykola Demko

Einleitung

Die Frage der Implementierung von Templates als Standard der Sprache wurde im mql5.com-Forum oft gestellt. Nachdem ich bei den Entwicklern von MQL5 auf Ablehnung stieß, begann mein Interesse an der Implementierung von Templates mithilfe benutzerdefinierter Methoden zu wachsen. Das Ergebnis meiner Studien wird in diesem Beitrag vorgestellt.



Zur Geschichte von C und C++

Die Sprache C wurde von Anfang an dahingehend entwickelt, die Möglichkeit der Durchführung von Systemaufgaben zu bieten. Die Schöpfer der Sprache C implementierten kein abstraktes Modell einer Ausführungsumgebung der Sprache, sondern implementierten einfach Funktionen für die Bedürfnisse von Systemprogrammierern. Dabei handelt es sich vorrangig um Methoden zur direkten Arbeit mit dem Speicher, strukturelle Konstruktionen zur Steuerung und Modulmanagement von Anwendungen.

Im Wesentlichen wurde nichts Weiteres in die Sprache aufgenommen. Alles Weitere wurde in die Laufzeitbibliothek ausgelagert. Deshalb behaupten böse Zungen manchmal, C wäre ein struktureller Assembler. Doch unabhängig von diesen Behauptungen erwies sich der Ansatz als äußerst erfolgreich. Ihm ist es zu verdanken, dass ein neues Niveau der Entsprechung zwischen Einfachheit und Wirksamkeit der Sprache erreicht werden konnte. 

So erschien C als universelle Sprache für die Systemprogrammierung. Doch sie blieb nicht in diesen Grenzen. Ende der 80er Jahre verdrängte C Fortran von der Führungsposition und gelangte an große Beliebtheit bei Programmierern auf der ganzen Welt und wurde häufig für verschiedenste Anwendungen genutzt. Einen wesentlichen Beitrag zu ihrer Beliebtheit leistete die Verbreitung von Unix (und somit der Sprache C) in Universitäten, in denen eine neue Generation von Programmierern ausgebildet wurde.

Doch wenn alles so gut aussieht, warum werden dann all die anderen Sprachen noch verwendet und wodurch wird ihre Existenz unterstützt? Die Achillessehne von C ist, dass die Sprache für Probleme, die seit den 90er Jahren auftreten, auf zu niedriger Ebene tätig ist. Dieses Problem hat zwei Aspekte.

Einerseits enthält die Sprache Mittel auf zu niedriger Ebene, allem voran ihre Arbeit mit dem Speicher und die Adressarithmetik. Somit verursache eine Änderung der Bitanzahl von Prozessoren zahlreiche Probleme für viele C-Anwendungen. Andererseits enthält C zu wenige Mittel auf hoher Ebene – abstrakte Daten- und Objekttypen, Polymorphie und Verarbeitung von Ausnahmen. Somit dominiert in C-Anwendungen eine Technik zur Umsetzung einer Aufgabe oft über ihre materielle Seite.

Erste Versuche, diese Nachteile zu beheben, wurden in den frühen 80ern unternommen. Zu dieser Zeit begann Bjarne Stroustrup bei AT&T Bell Labs mit der Entwicklung einer Erweiterung der Sprache C mit dem Namen "C mit Klassen". Der Stil der Entwicklung entsprach dem Geist der Erschaffung der Sprache selbst – Hinzufügen verschiedener Funktionen, damit bestimmte Gruppen von Menschen bequemer zusammenarbeiten können.

Die wichtigste Innovation in C++ ist der Mechanismus von Klassen, der die Möglichkeit eröffnet, neue Datentypen zu nutzen. Ein Programmierer beschreibt die interne Darstellung eines Klassenobjekts und den Satz von Methoden und Funktionen für den Zugriff auf diese Darstellung. Eines der Hauptziele bei der Erschaffung von C++ war die Stärkung der Wiederverwendung von bereits geschriebenem Code.

Doch die Innovation der Sprache C++ besteht nicht ausschließlich aus der Einführung von Klassen. Es wurde ein Mechanismus für die strukturelle Verarbeitung von Ausnahmen (das Fehlen eines solchen Mechanismus erschwerte die Entwicklung ausfallsicherer Anwendungen), ein Mechanismus für Templates und vieles andere implementiert. Somit war die Entwicklung der Sprache hauptsächlich auf die Erweiterung ihrer Möglichkeiten durch die Einführung neuer Konstruktionen auf hoher Ebene unter Beibehaltung der vollständigen Kompatibilität mit ANSI C ausgerichtet. 



Template als Mechanismus der Makro-Substitution

Um die Implementierung eines Templates in MQL5 zu verstehen, müssen Sie zunächst verstehen, wie sie in C++ funktionieren.

Sehen wir uns die Definition an.

Templates sind ein Merkmal der Programmiersprache C++, das es Funktionen und Klassen ermöglicht, mit generischen Typen zu arbeiten. Damit wird es einer Funktion oder Klasse ermöglicht, mit vielen unterschiedlichen Datentypen zu arbeiten, ohne dass sie für jeden Typ neu geschrieben werden müssten.

MQL5 verfügt über keine Templates, doch das bedeutet nicht, dass es unmöglich ist, einen Programmierstil mit Templates zu nutzen. Der Mechanismus für Templates in der Sprache C++ ist im Wesentlichen ein ausgeklügelter Mechanismus zur Makro-Generierung, der tief in der Sprache eingebettet ist. In anderen Worten: Wenn ein Programmierer ein Template verwendet, bestimmt der Compiler den Datentyp, in dem die entsprechende Funktion aufgerufen wird, nicht den, in dem sie deklariert wird.

Templates wurden in C++ eingeführt, um die von Programmierern geschriebenen Codemengen zu reduzieren. Doch Sie sollten nicht vergessen, dass ein Code, der von einem Programmierer per Tastatur eingetippt wird, nicht derselbe ist, der vom Compiler erstellt wird. Der Template-Mechanismus selbst verringert nicht die Größe von Programmen, sondern nur die Größe ihres Quellcodes. Deshalb ist die wichtigste Aufgabe, die durch die Nutzung von Templates gelöst wird, die Verminderung des von Programmierern getippten Codes.

Da der Maschinencode während der Kompilierung generiert wird, können herkömmliche Programmierer nicht sehen, ob der Code einer Funktion einmalig oder mehrfach generiert wird. Während der Kompilierung des Codes eines Templates wird der Code der Funktion so oft erstellt, wie es Typen gibt, deren Template verwendet wurde. Im Wesentlichen ist ein Template nichts anderes als Überschreiben während der Kompilierungsphase.

Der zweite Aspekt der Einführung von Templates in C++ ist die Zuweisung von Speicher. Die Sache ist, dass Speicher in C statisch zugewiesen wird. Um diese Zuweisung flexibler zu machen, wird ein Template verwendet, das die Größe des Speichers für Arrays festlegt. Doch dieser Aspekt wurde bereits von den Entwicklern von MQL4 in Form von dynamischen Arrays umgesetzt und ist auch in MQL5 in Form von dynamischen Objekten vorhanden.

Somit bleibt nur das Problem der Ersetzung von Typen ungelöst. Die Entwickler von MQL5 weigern sich, dieses Problem zu lösen, indem sie sich darauf berufen, dass die Nutzung eines Mechanismus zur Ersetzung von Templates ein Knacken des Compilers ermöglichen würde, was einen Decompiler notwendig machen würde.

Sie wissen es sicherlich besser. Uns bleibt nur eine Wahl: Das Paradigma benutzerdefiniert zu implementieren.

Zuallererst möchte ich festhalten, dass wir den Compiler oder die Standards der Sprache nicht verändern werden. Ich schlage vor, die Herangehensweise an Templates selbst zu ändern. Wenn wir während der Kompilierungsphase keine Templates erstellen können, bedeutet dies nicht, dass es uns nicht erlaubt ist, den Maschinencode zu schreiben. Ich schlage vor, die Verwendung des Templates von der Generierung des Binärcodes zu dem Teil zu verschieben, in dem der Textcode geschrieben wird. Nennen wir diesen Ansatz "Pseudo-Templates".



Pseudo-Templates

Ein Pseudo-Template hat im Vergleich zu einem C++-Template Vor- und Nachteile. Zu den Nachteilen gehören zusätzliche Eingriffe bei der Verschiebung von Dateien. Zu den Vorteilen gehören Möglichkeiten, die vielseitiger sind, als es durch die Standards der Sprache bestimmt wird. Widmen wir uns nun aber der Praxis.

Um Pseudo-Templates nutzen zu können, brauchen wir ein Gegenstück zum Präprozessor. Zu diesem Zweck nutzen wir das Script 'Templates'. Hier sind die Grundanforderungen an das Script: Es muss eine angegebene Datei lesen (und die Datenstruktur beibehalten), ein Template finden und es mit den angegebenen Typen ersetzen.

An dieser Stelle muss ich eine Bemerkung machen. Da wir einen Überschreibmechanismus anstatt Templates nutzen werden, wird der Code so oft erneut geschrieben, wie es Typen gibt, die überschrieben werden sollen. In anderen Worten: Die Ersetzung wird im gesamten zu analysierenden Code durchgeführt. Anschließend wird der Code durch das Script mehrfach neu geschrieben und schafft dabei jedes Mal eine neue Ersetzung. Das ist "Handarbeit, die von Maschinen durchgeführt wird".



Entwickeln des Script-Codes

Bestimmen wir die erforderlichen Eingabevariablen:

  1. Name einer zu verarbeitenden Datei.
  2. Variable zum Speichern des zu überschreibenden Datentyps.
  3. Name eines Templates, das anstelle realer Datentypen verwendet wird.
input string folder="Example templat";//name of file for processing

input string type="long;double;datetime;string"
                 ;//names of custom types, separator ";"
string TEMPLAT="_XXX_";// template name

Damit das Script nur einen Teil des Codes vervielfacht, bestimmen Sie die Namen von Markern. Der öffnende Marker markiert den Anfang des zu verarbeitenden Teils, der schließende sein Ende.

Während der Verwendung des Scripts bin ich auf Probleme beim Lesen der Marker gestoßen.

Bei der Analyse fand ich heraus, dass beim Formatieren eines Dokuments in MetaEditor oft ein Leerzeichen oder Tabulator (je nach Situation) in den Kommentarzeilen hinzugefügt wird. Das Problem wurde durch Löschen der Leerzeichen vor und hinter einem bedeutsamen Zeichen bei der Bestimmung von Markern gelöst. Diese Funktion wird im Script automatisch umgesetzt, doch es gibt noch eine Anmerkung dazu.

Der Name eines Markers darf nicht mit einem Leerzeichen beginnen oder enden.

Ein schließender Marker ist nicht zwangsläufig erforderlich. Wenn er fehlt, wird der Code bis zum Ende der Datei ausgeführt. Allerdings muss es einen öffnenden Marker geben. Da die Namen von Markern konstant sind, nutze ich die Präprozessor-Direktive #define anstelle von Variablen.

#define startread "//start point"
#define endread "//end point"

Um ein Array von Typen zu formen, habe ich die Funktion void ParserInputType(int i,string &type_d[],string text) erstellt, die das Array type_dates[] mithilfe der Variable 'type' mit Werten befüllt.    

Sobald das Script den Namen einer Datei und Marker erhält, beginnt es, die Datei zu lesen. Um die Formatierung des Dokuments zu speichern, liest das Script die Informationen Zeile für Zeile und speichert gefundene Zeilen im Array.

Natürlich können Sie alles in eine Variable stecken, doch in diesem Fall verlieren Sie alle Trennungen und der Text wird zu einer Endloszeile. Deshalb nutzt die Funktion zum Lesen der Datei das Array von Strings, das seine Größe bei jedem Durchlauf des Erhalts eines neuen Strings ändert.

//+------------------------------------------------------------------+
//| downloading file                                                 |
//+------------------------------------------------------------------+
void ReadFile()
  {
   string subfolder="Templates";
   int han=FileOpen(subfolder+"\\"+folder+".mqh",FILE_READ|FILE_SHARE_READ|FILE_TXT|FILE_ANSI,"\r"); 
   if(han!=INVALID_HANDLE)
     {
      string temp="";
      //--- scrolling file to the starting point
      do {temp=FileReadString(han);StringTrimLeft(temp);StringTrimRight(temp);}
      while(startread!=temp);

      string text=""; int size;
      //--- reading the file to the array until a break point or the end of the file
      while(!FileIsEnding(han))
        {
         temp=text=FileReadString(han);
         // deleting symbols of tabulation to check the end
         StringTrimLeft(temp);StringTrimRight(temp);
         if(endread==temp)break;
         // flushing data to the array
         if(text!="")
           {
            size=ArraySize(fdates);
            ArrayResize(fdates,size+1);
            fdates[size]=text;
           }
        }
      FileClose(han);
     }
   else
     {
      Print("File open failed"+subfolder+"\\"+folder+".mqh, error",GetLastError());
      flagnew=true;
     }
  }

Zur Vereinfachung der Nutzung wird die Datei im FILE_SHARE_READ-Modus geöffnet. Dies gibt uns die Möglichkeit, das Script zu starten, ohne die bearbeitete Datei zu schließen. Die Dateierweiterung ist mit 'mqh' festgelegt. Auf diese Weise liest das Script direkt den Text des Codes, der in der Include-Datei gespeichert ist. Das liegt daran, dass eine Datei mit der Erweiterung 'mqh' im Wesentlichen eine Textdatei ist. Dies können Sie überprüfen, indem Sie die Datei einfach in 'txt' umbenennen und die 'mqh'-Datei mit einem beliebigen Texteditor öffnen. 

Am Ende des Lesevorgangs entspricht die Länge des Arrays der Anzahl der Zeilen zwischen dem Start- und dem End-Marker.

Der Name der geöffneten Datei muss die Erweiterung "templat" haben. Andernfalls wird die Ausgangsdatei überschrieben und alle Informationen gehen verloren.

Kehren wir nun zur Analyse der Informationen zurück. Die Funktion, die Informationen analysiert und ersetzt, wird aus der Funktion void WriteFile(int count) zum Schreiben in eine Datei aufgerufen. Kommentare sind im Inneren der Funktion aufgeführt.

void WriteFile(int count)
  {
   ...
   if(han!=INVALID_HANDLE)
     {
      if(flagnew)// if the file cannot be read
        {
         ...
        }
      else
        {// if the file exists
         ArrayResize(tempfdates,count);
         int count_type=ArraySize(type_dates);
         //--- the cycle rewrites the contents of the file for each type of the type_dates template
         for(int j=0;j<count_type;j++)
           {
            for(int i=0;i<count;i++) // copy data into the temporary array
               tempfdates[i]=fdates[i];
            for(int i=0;i<count;i++) // replace templates with types
               Replace(tempfdates,i,j);

            for(int i=0;i<count;i++)
               FileWrite(han,tempfdates[i]); // flushing array in the file
           }
        }
     ...
  }

Da die Daten an ihrem angestammten Ort ersetzt werden und das Array nach der Transformation verändert wird, arbeiten wir mit einer Kopie davon. Hier legen wir die Größe des Arrays tempfdates[] für die temporäre Speicherung von Daten fest und befüllen es gemäß dem Beispiel fdates[].

Anschließend wird die Ersetzung von Templates mithilfe der Funktion Replace() durchgeführt. Die Parameter der Funktion sind: zu verarbeitendes Array (in dem die Ersetzung des Templates durchgeführt wird), Zeilenzähler i (für die Verschiebung innerhalb des Arrays) und Typenzähler j (für die Navigation durch das Typen-Array).

Da wir zwei geschachtelte Zyklen haben, wird der Quellcode so oft gedruckt, wie Typen festgelegt sind.

//+------------------------------------------------------------------+
//| replacing templates with types                                   |
//+------------------------------------------------------------------+
void Replace(string &temp_m[],int i,int j)
  {
   if(i>=ArraySize(temp_m))return;
   if(j<ArraySize(type_dates))
      StringReplac(temp_m[i],TEMPLAT,type_dates[j]);// replacing  templat with types   
  }

Die Funktion Replace() enthält Überprüfungen (zum Vermeiden des Aufrufs eines nicht existierenden Index eines Arrays) und ruft die geschachtelte Funktion StringReplac() auf. Es gibt einen Grund, aus dem der Name der Funktion ähnlich dem der Standardfunktion StringReplace ist, sie haben auch die gleiche Anzahl von Parametern.

Somit können wir durch Hinzufügen des Buchstaben "e" die gesamte Ersetzungslogik verändern. Die Standardfunktion nimmt den Wert des Musters 'find' und ersetzt ihn durch den festgelegten String 'replacement'. Meine Funktion dient nicht nur dem Ersetzen, sondern analysiert auch, ob es Symbole vor 'find' gibt (d. h., sie prüft, ob 'find' Teil eines Wortes ist). Ist dies der Fall, ersetzt sie 'find' durch 'replacement', allerdings groß geschrieben. Andernfalls wird die Ersetzung unverändert durchgeführt. Deshalb können Sie sie neben dem Festlegen von Typen auch für die Namen überschriebener Daten nutzen.



Innovationen

Lassen Sie mich nun über die Innovationen erzählen, die während des Gebrauchs dazukamen. Ich habe bereits erwähnt, dass es Probleme beim Lesen von Markern während der Verwendung des Scripts gab.

Das Problem wird durch den folgenden Code innerhalb der Funktion void ReadFile() gelöst:

      string temp="";
      //--- scrolling the file to the start point
      do {temp=FileReadString(han);StringTrimLeft(temp);StringTrimRight(temp);}
      while(startread!=temp);

Der Zyklus selbst wurde in der vorhergehenden Version implementiert, doch das Abschneiden von Tabulierungszeichen mithilfe der Funktionen StringTrimLeft() und StringTrimRight() erschien erst in der verbesserten Version.

Zu den weiteren Innovationen gehört das Abschneiden der Erweiterung "templat" vom Namen der Ausgabedatei, sodass die Ausgabedatei direkt einsatzbereit ist. Dies wird mithilfe der Funktion zum Löschen eines bestimmten Beispiels aus einem festgelegten String umgesetzt.

Code der Löschfunktion:

//+------------------------------------------------------------------+
//| Deleting the 'find' template from the 'text' string              |
//+------------------------------------------------------------------+
string StringDel(string text,const string find)
  {
   string str=text;
   StringReplace(str,find,"");
   return(str);
  }

Der Code, der Dateinamen abschneidet, befindet sich in der Funktion void WriteFile(int count):

   string newfolder;
   if(flagnew)newfolder=folder;// if it is the first start, create an empty file of pre-template
   else newfolder=StringDel(folder," templat");// or create the output file according to the template

Zusätzlich wird der Modus zum Vorbereiten eines Vorab-Templates eingeführt. Wenn die erforderliche Datei im Verzeichnis Files/Templates nicht existiert, wird sie als Vorab-Template-Datei erstellt.

Beispiel:

//#define _XXX_ long
 
//this is the start point
 _XXX_
//this is the end point

Der Code zum Erstellen dieser Zeilen befindet sich in der Funktion void WriteFile(int count):

      if(flagnew)// if the file couldn't be read
        {// fill the template file with the pre-template
         FileWrite(han,"#define "+TEMPLAT+" "+type_dates[0]);
         FileWrite(han," ");
         FileWrite(han,startread);
         FileWrite(han," "+TEMPLAT);
         FileWrite(han,endread);
         Print("Creating pre-template "+subfolder+"\\"+folder+".mqh");
        }

Die Ausführung des Codes wird durch die globale Variable flagnew geschützt, die den Wert 'true' annimmt, wenn es einen Fehler beim Lesen der Datei gab.

Bei der Verwendung des Scripts habe ich ein zusätzliches Template hinzugefügt. Der Prozess der Verbindung des zweiten Templates ist der gleiche. Funktionen, die Veränderungen benötigen, werden näher an der Funktion OnStart() für die Verbindung eines zusätzlichen Templates platziert. Ein Pfad für die Verbindung neuer Templates wird erstellt. Somit haben wir eine Möglichkeit, so viele Templates zu verbinden, wie wir benötigen. Überprüfen wir nun die Arbeit.



Prüfung der Arbeit

Führen wir zunächst das Script zum Festlegen aller erforderlichen Parameter aus. Geben Sie im geöffneten Fenster den Dateinamen "Example templat" ein.

Füllen Sie die Felder der benutzerdefinierten Datentypen mithilfe des Trenners ';' aus.

Startfenster des Scripts

Sobald die Schaltfläche "OK" gedrückt wird, wird das Verzeichnis Templates erstellt. Es enthält die Vorab-Template-Datei "Example templat.mqh".

Dieses Ereignis wird mit der folgenden Meldung im Logbuch angezeigt:

Logbuchmeldungen

Passen wir das Vorab-Template an und führen das Script erneut aus. Diesmal existiert die Datei bereits im Verzeichnis Templates (sowie das Verzeichnis selbst), also wird die Meldung über den Fehler beim Öffnen der Datei nicht angezeigt. Die Ersetzung wird gemäß dem festgelegten Template durchgeführt:

//this_is_the_start_point
 _XXX_ Value_XXX_;
//this_is_the_end_point

Öffnen Sie die erstellte Datei "Example.mqh" erneut.

 long ValueLONG;
 double ValueDOUBLE;
 datetime ValueDATETIME;
 string ValueSTRING;

Wie Sie sehen können, entstehen 4 Zeilen aus einer Zeile in Übereinstimmung mit der Anzahl der Typen, die wir als Parameter übergeben haben. Schreiben Sie nun die folgenden zwei Zeilen in die Template-Datei:

//this_is_the_start_point
 _XXX_ Value_XXX_;
 _XXX_ Type_XXX_;
//this_is_the_end_point

Das Ergebnis demonstriert klar die Logik der Arbeit des Scripts.

Als Erstes wird der gesamte Code mit einem Datentyp neu geschrieben, anschließend wird ein anderer Typ verarbeitet. Dies wird wiederholt, bis alle Typen verarbeitet wurden.

 long ValueLONG;
 long TypeLONG;
 double ValueDOUBLE;
 double TypeDOUBLE;
 datetime ValueDATETIME;
 datetime TypeDATETIME;
 string ValueSTRING;
 string TypeSTRING;

Fügen Sie nun das zweite Template in den Text des Musters ein.

//this_is_the_start_point
 _XXX_ Value_XXX_(_xxx_ ind){return((_XXX_)ind);};
 _XXX_ Type_XXX_(_xxx_ ind){return((_XXX_)ind);};

 //this_is_the_end_button

Ergebnis:

 long ValueLONG(int ind){return((long)ind);};
 long TypeLONG(int ind){return((long)ind);};
 
 double ValueDOUBLE(float ind){return((double)ind);};
 double TypeDOUBLE(float ind){return((double)ind);};
 
 datetime ValueDATETIME(int ind){return((datetime)ind);};
 datetime TypeDATETIME(int ind){return((datetime)ind);};
 
 string ValueSTRING(string ind){return((string)ind);};
 string TypeSTRING(string ind){return((string)ind);};

Im letzten Muster habe ich absichtlich ein Leerzeichen hinter die letzte Zeile geschrieben. Dieses Leerzeichen demonstriert, wo das Script die Verarbeitung eines Typen beendet und mit der Verarbeitung eines anderen beginnt. Bezüglich des zweiten Templates können wir festhalten, dass die Verarbeitung der Typen genauso wie beim ersten Template durchgeführt wird. Wird kein entsprechender Typ für einen Typen des ersten Templates gefunden, wird nichts gedruckt.

Nun möchte ich die Frage des Debuggens des Codes klären. Die aufgeführten Beispiele sind ziemlich einfach zu debuggen. Während der Programmierung müssen Sie möglicherweise einen ziemlich großen Teil des Codes debuggen und ihn nach dem Abschluss vervielfachen. Dafür gibt es eine reservierte kommentierte Zeile im Vorab-Template: "//#define _XXX_ long".

Wenn Sie die Kommentare entfernen, wird aus unserem Template ein echter Typ. In anderen Worten: Wir sagen dem Compiler, wie das Template zu interpretieren ist.

Leider können wir auf diese Weise nicht alle Typen debuggen. Doch wir können einen Typ debuggen und dann den Typen des Templates in 'define' ändern. So können wir alle Typen einen nach dem anderen debuggen. Natürlich müssen wir die Datei zum Debuggen in das Verzeichnis der aufgerufenen Datei oder in das Include-Verzeichnis verschieben. Das ist der unpraktische Debugging-Vorgang, den ich vorher erwähnt habe, als ich über die Nachteile der Pseudo-Templates sprach.



Fazit

Als Fazit möchte ich festhalten, dass die Idee der Nutzung von Pseudo-Templates zwar interessant und ziemlich produktiv ist, allerdings auch nur eine kleine anfängliche Umsetzung darstellt. Obwohl der oben dargelegte Code funktioniert und mir beim Schreiben viel Zeit erspart hat, sind viele Fragen weiterhin offen. Die erste Frage ist die Frage der Entwicklung von Standards.

Mein Script setzt die Ersetzung von Templates in Blöcken um. Doch dieser Ansatz ist nicht verpflichtend. Sie können einen komplexeren Analysator erstellen, der bestimmte Regeln interpretiert. Dies ist nur der Ausgangspunkt. Ich hoffe auf eine lebhafte Diskussion. Ideen leben von Konflikten. Viel Erfolg!

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

Beigefügte Dateien |
templates.mq5 (9.91 KB)
Die Rolle von statistischen Verteilungen für die Arbeit eines Händlers Die Rolle von statistischen Verteilungen für die Arbeit eines Händlers
Dieser Beitrag ist eine logische Fortsetzung meines Beitrags Statistische Verteilungen von Wahrscheinlichkeiten in MQL5, in dem die Klassen für die Arbeit mit einigen theoretischen statistischen Verteilungen dargelegt wurden. Da wir nun über die theoretische Grundlage verfügen, schlage ich vor, dass wir direkt mit realen Datensätzen fortfahren und versuchen, diese Grundlage für Informationszwecke zu nutzen.
Verwendung von Indikatoren in MetaTrader 5 mit dem Machine Learning Framework ENCOG für die Prognostizierung von Zeitreihen Verwendung von Indikatoren in MetaTrader 5 mit dem Machine Learning Framework ENCOG für die Prognostizierung von Zeitreihen
In diesem Beitrag wird die Verbindung von MetaTrader 5 mit ENCOG, einem erweiterten neuronalen Netzwerk und Machine Learning Framework, vorgestellt. Er enthält die Beschreibung und Implementierung eines einfachen neuronalen Netzwerkindikators auf Basis technischer Standardindikatoren und eines Expert Advisors auf Basis eines neuronalen Indikators. Alle Quellcodes, kompilierten Binärdateien, DLLs und Beispiele für eingelernte Netzwerke sind an diesen Beitrag angehängt.
Universeller Expert Advisor: Handelsmodi von Strategien (Teil 1) Universeller Expert Advisor: Handelsmodi von Strategien (Teil 1)
Jeder Entwickler von Expert Advisors, ungeachtet seiner Programmierfähigkeiten, wird mit den gleichen Aufgaben und algorithmischen Problemen konfrontiert, die für einen sicheren Handelsprozess auf diese oder jene Weise gelöst werden müssen. Dieser Beitrag beschreibt die Möglichkeiten der 'Trading-Engine' CStrategy, die die Lösung dieser Aufgaben übernehmen und dem Nutzer geeignete Mechanismen zur Beschreibung seiner Handelsidee anbieten kann.
Alarm und Benachrichtigung für Externe Indikatoren Alarm und Benachrichtigung für Externe Indikatoren
Beim Arbeiten kann ein Händler mit der folgenden Situation konfrontiert werden: es ist notwendig, einen "Alarm" oder eine SMS-Nachricht am Bildschirm (in einem Chartfenster) zu erhalten, um einen Hinweis auf ein erscheinendes Signal eines Indikators zu bekommen. Der Artikel beinhaltet ein Beispiel zur Anzeige von Informationen über grafische Objekte, die durch einen externen Indikator kreiert wurden.