English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Die Interaktion von MetaTrader 5 und MATLAB

Die Interaktion von MetaTrader 5 und MATLAB

MetaTrader 5Beispiele | 12 Februar 2016, 11:50
1 398 0
Andrey Emelyanov
Andrey Emelyanov

Einleitung

Die MQL-Community hat meinen ersten Beitrag Interaktion zwischen MetaTrader 4 und der MATLAB Engine (Virtuelle MATLAB Maschine) interessiert aufgenommen. Einige Leser (1Q2W3E4R5T) konnten dieses Projekt sogar von Borland auf VS2008 verschieben. Doch Zeit ist unerbittlich und (traurig aber wahr) MetaTrader 4 verschwindet langsam und macht Platz für seinen Nachfolger MetaTrader 5 mit MQL5, der Zeiger und das dynamische Memory einführte. 

Dank dieser Innovationen sind wir jetzt in der Lage eine universelle Library der Interaktion mit der MATLAB Engine virtuellen Maschine zu schreiben und Libraries, die von MATLAB generiert werden, direkt mit MetaTrader 5 zu verknüpfen. Dieser Beitrag behandelt solche eine Funktionalität. Er führt auf logische Art und Weise den vorigen Beitrag weiter und behandelt das Problem der Interaktion zwischen MetaTrader 5 und MATLAB umfassender.

Um nicht vorbereiteten Lesern den Umfang dieses Beitrags verständlicher zu machen, teilen wir ihn in drei Teile auf: Theorie, Referenz und Praxis. Der Theorieteil beschäftigt sich mit den in MQL5 und MATLAB verwendeten Daten sowie auch ihrer gegenseitigen Konvertierung. Im Referenzteil erfahren Sie mehr über die linguistischen Strukturen und die Syntax der Funktionen, die zur Erzeugung eines DLL notwendig sind. Und im Praxisteil sehen wir uns die "Fallen" dieser Interaktion genauer an.

Leser, die bereits über Erfahrung verfügen, können den Theorie- und Referenzteil gerne überspringen und gleiche zum Praxisteil übergehen. Allen anderen wird dringend empfohlen, die Bereiche Theorie und Referenz zu lesen und erst dann zum Praxisteil überzugehen. Und die Bücher, die in der "Bibliographie" aufgeführt sind, sollten auch gelesen werden.

1. Theorie

1.1 Datentypen in MATLAB und MQL5

1.1.1 Einfache Datentypen

Fangen wir an.

Zu allererst müssen wir uns mit dem Innenleben von MQL5 und MATLAB vertraut machen. Nach einer oberflächlichen Untersuchung der Variablentypen, stellen wir fest, dass sie sich fast gleichen:

MQL5
Größe in Byte
Minimalwert
Maximalwert
 MATLAB
char
1
-128
127
Array int8/char
uchar
1
0
255
Array int8/char
bool
1
0(false)
1(true)
Array logical
short
2
-32768
32767
Array int16
ushort
2
0
65535
Array int16
int
4
-2147483648
2147483647
Array int32
uint
4
0
4294967295
Array int32
long 8
-9223372036854775808
9223372036854775807 Array int64
ulong 8
0
18446744073709551615
Array int64
float 4
1.175494351e-38
3.402823466e+38
Array single
double
8
2.225073858507201e-308
1.7976931348623158e+308
Array double

Tabelle 1 Datentypen in MATLAB und MQL5

Es gibt jedoch einen wichtigen Unterschied: Variablen in MQL5 können einfach oder zusammengesetzt sein (komplex), und in MATLAB sind alle Variablen multi-dimensional (komplex), d.h. eine Matrix. Diesen Unterschied sollten Sie immer bedenken!

1.1.2 Komplexe Datentypen

Es gibt vier komplexe Datentypen in MQL5: Arrays, Strings, Strukturen und Klassen. Ein komplexer Datentyp ist eine Anordnung verschiedener einfacher Datentypen, die in einem Memory-Block von gewisser Länge vereint sind. Bei der Arbeit mit solchen Daten müssen Sie stets entweder die Größe des Memory-Blocks kennen (in Byte) oder die Anzahl der Elemente (außer bei Klassen). Uns interessieren hier nur Arrays und Strings, da das Einbringen von Klassen und MQL5 Strukturen in MATLAB unsinnig ist.

Bei der Übertragung von Arrays jedweden Typs müssen Sie folgendes kennen: Typ (Dimension) und Anzahl der Elemente, die die ArraySize() Funktion verwenden. Besondere Aufmerksamkeit verdient die Indizierung in MetaTrader 5 - meistens rückwärts (d.h. das erste Element enthält jüngere Daten als das zweite usw.). Dies kann mit Hilfe der ArrayIsSeries() Funktion überprüft werden. Und MATLAB arbeitet mit folgender Indizierung: das erste Element enthält ältere Daten als das zweite, usw. - daher müssen Sie Ihre Arrays vor dem Abschicken an MATLAB "umgekehrt anordnen", wenn AS_SERIES = TRUE gemeldet werden soll. Vor diesem Hintergrund können wir also zusammenfassen:

  • Arrays "unsichtbar" für MQL5-Programme "umgekehrt anordnen", außer bei Arrays des Typs char und bei zweidimensionalen Arrays - sie bleiben unverändert.
  • Arrays "unsichtbar" von MATLAB umgekehrt anordnen und ihnen die Flagge AS_SERIES mit TRUE zuweisen, außer bei Arrays des Typs char und bei zweidimensionalen Arrays - sie bleiben unverändert.
  • In jedem Array des MQL5-Programms, das gemäß der "umgekehrten" Indizierung erzeugt wurde, muss die AS_SERIES Flagge TRUE sein, außer bei Arrays des Typs char und bei zweidimensionalen Arrays - sie bleiben unverändert.  

Doch es gibt noch weitere Beschränkungen in der Arbeit mit Arrays Bei der Arbeit mit multi-dimensionalen Arrays, oder genauer gesagt Matrices, insbesondere von MATLAB, führen wir die Beschränkung nur für zweidimensionale Arrays ein. Hier darf die AS_SERIES Flagge nicht TRUE sein, und daher sind derartige Arrays nicht "umgekehrt".

Nicht vergessen: Die Strings in MQL5 sind keine Arrays der char Typ-Elemente. Bei der Übertragung von Strings entsteht hier also ein kleines Problem: MQL5 codiert Strings mit Hilfe von Unicode, und MATLAB verwendet die ANSI-Kodierung. Bevor also ein String übertragen wird, sollte er in ein Array mit ANSI-Zeichen konvertiert werden und zwar mit Hilfe der StringToCharArray() Funktion. Und umgekehrt, wenn Sie ein Zeichenarray aus MATLAB erhalten, müssen sie es mit Hilfe der CharArrayToString() Funktion ebenfalls konvertieren (vgl. Tabelle 2). Um Verwirrung zu vermeiden, also noch einmal: Alle Strings in MQL5-Programmen mit Unicode speichern, keine Arrays des char Typs.

1.2 Vergleich der MQL5 und MATLAB Datentypen

Um die Menge der Funktionen zu verringern und den Library-Algorithmus zu vereinfachen, senken wir auch die Menge der Typen mit Hilfe automatischer Konvertierung, die sich nicht auf die Datenintegrität auswirkt. Die folgende Tabelle zeigt die Regel der Datentypkonvertierung von MQL5 in MATLAB:

 MQL5  MatLab Entsprechung
char 
uchar
Array char
bool
Array logical
short
ushort
int
uint
Array int32
long
ulong
Array int64*
float
double
Array double
string
Array char mit Hilfe der StringToCharArray() <=> CharArrayToString() Funktionen

* Diese Konvertierung führt zu Einbußen in der Exaktheit. Wir verwenden sie daher hier nicht, doch Sie können in Ihren Programmen durchaus mit einer derartigen Konvertierung arbeiten.

Tabelle 2 Vergleich der MQL5 und MATLAB Datentypen

Jetzt kennen Sie die in MQL5 und MATLAB verwendeten Datentypen. Und Sie wissen, mit welchen "Fallen" Sie bei der Datenübertragung zu rechnen haben und wie Sie sie kompetent umgehen können. Nun müssen Sie noch die MATLAB Engine API kennen lernen und sich mit dem MATLAB Compiler 4 vertraut machen.

2. MATLAB Engine API Referenz, MATLAB Compiler 4 Referenz und C++ Input/Output Library Referenz

In diesem Abschnitt lernen Sie die wichtigsten Funktionen der MATLAB Engine API, die Features des MATLAB Compiler 4 und einige nützliche Funktionen der C++ Standard Input/Output Library kennen. Also los.

2.1 MATLAB Engine API und MCR Funktionen

Die MATLAB Engine - ist das externe Interface, das andern Programmen gestattet, den MATLAB-Desktop zu verwenden. Es bietet voll-funktionsfähige Arbeit aller MATLAB Pakete ohne jegliche Einschränkung.

Obwohl es in der Dokumentation nicht erwähnt wird, doch in der Denkweise der Programmierer ist dieses Interface nur eine virtuelle Maschine - so wie PHP oder MySQL usw., die eine einfache und relativ schnelle Möglichkeit des Datenaustauschs zwischen MetaTrader 4/5 und MATLAB ermöglicht.  

Entwickler empfehlen diese Methode der Verknüpfung externer Programme mit dem MATLAB-Paket. Das Interface besteht aus sechs Funktionen:

Engine *pEng = engOpen(NULL) — diese Funktion ruft den MATLAB-Desktop auf. Der Parameter ist immer NULL, sodass ein Zeiger auf dem Desktop-Descriptor geliefert wird.

int exitCode = engClose(Engine *pEng) — diese Funktion schleißt den Desktop und liefert die Zahl der restlichen Anwender auf dem MATLAB-Desktop, wobei:

  • Engine *pEng — der Zeiger auf den Desktop-Descriptor ist.  

mxArray *mxVector = mxCreateDoubleMatrix(int m, int n, int ComplexFlag) — diese Funktion erzeugt eine Variable (Matrix) des MATLAB-Desktops und liefert der Variable (Matrix) einen Zeiger, wobei:

  • mxArray *mxVector — der Zeiger auf die Matrix-Variable ist.  
  • int m — Anzahl der Reihen.  
  • int n — Anzahl der Spalten.  
  • ComplexFlag — Typ einer komplexen Zahl, für MetaTrader 4/5 mxREAL.
void = mxDestroyArray(mxArray *mxVector) — diese Funktion vernichtet die MATLAB-Matrix und wird zur Löschung des Memory benötigt, wobei:
  • mxArray *mxVector — der Zeiger auf die Matrix-Variable ist..  
int = engPutVariable(Engine *pEng, char *Name, mxArray *mxVector) — diese Funktion sendet Variablen an den Desktop. Sie müssen nämlich nicht nur Variablen des mxArray Typs erzeugen, sondern sie auch an MATLAB senden, wobei:
  • Engine *pEng — der Zeiger auf den Desktop-Descriptor ist.  
  • char *Name — der Variablen-Name des char Typs im MATLAB-Desktop ist.  
  • mxArray *mxVector — der Zeiger auf die Matrix-Variable ist.  
mxArray *mxVector = engGetVariable(Engine *pEng, char *Name) — diese Funktion holt sich Variablen vom Desktop - das Gegenteil der vorigen Funktion. Es werden nur Variablen des Typs mxArray akzeptiert, wobei:
  • mxArray *mxVector — der Zeiger auf die Matrix-Variable ist.  
  • Engine *pEng — der Zeiger auf den Desktop-Descriptor ist.  
  • char *Name — der Variablen-Name des char Typs im MATLAB-Desktop ist.  
double *p = mxGetPr(mxArray *mxVector) — diese Funktion holt sich Zeiger auf Werte-Arrays und wird zur Kopie von Daten, zusammen mit memcpy()verwendet (vgl. Abschnitt 2.3 C++ Standard Input/Output Library), wobei:
  • double *p — der Zeiger auf das Array des Typs double ist.  
  • mxArray *mxVector — der Zeiger auf die Matrix-Variable ist.  
int = engEvalString(Engine *pEng, char *Command) — diese Funktion schickt Befehle an den MATLAB-Desktop, wobei:
  • Engine *pEng — der Zeiger auf den Desktop-Descriptor ist.  
  • char *Command — der Befehl für MATLAB, String vom Typ char ist.  

Vielleicht haben Sie bemerkt, dass die MATLAB Engine API Ihnen die Erzeugung einer mxArray Struktur nur für double Typen erlaubt. Diese Einschränkung wirkt sich aber nicht nachteilig auf Ihre Möglichkeiten aus, sondern betrifft nur den Algorithmus Ihrer Library.

MCR (MCR instance) — ist die spezielle Library des MATLAB-Paket, mit der man eigenständige Anwendungen/public Libraries, die von der MATLAB Umgebung uf jedem x-beliebigen Computer erzeugt wurde, laufen lassen kann. Beachten Sie Bitte: Selbst wenn Sie ein vollständiges MATLAB-Paket haben, müssen Sie trotzdem noch die MCR-Library installieren, indem Sie die MCRInstaller.exe DAtei ablaufen lassen, die sich im <MATLAB>\Toolbox\compiler\deploy\win32 Ordner befindet. Bevor Sie also irgendeine public Library-Funktion, die von einer MATLAB Umgebung erzeugt wurde, aufrufen können, müssen sie zuerst die MCR-Initialisierungsfunktion aufrufen:
 
bool = mclInitializeApplication(const char **option, int count) – liefert TRUE, wenn der MCR Start erfolgreich war; ansonsten FALSE, wobei:

  • const char **option — der String der Optionen ist, wie in mcc - R; meistens NULL  
  • int count — der String der Größenoptionen ist, in der Regel 0.

Bei Beendigung der Arbeit mit der public Library, müssen Sie folgendes aufrufen:
bool = mclTerminateApplication(void) — liefert TRUE, wenn MCR erfolgreich geschlossen wurde.

2.2 MATLAB Compiler 4

Mit dem MATLAB Compiler können Sie von den M-Funktionen folgendes erzeugen:  

  • Eigenständige Anwendungen, die laufen, selbst wenn MATLAB nicht installiert ist.
  • C/C++ geteilte Libraries, die auf Endanwender-System auch ohne MATLAB verwendet werden können.

Der Compiler unterstützt die meisten Befehle und Pakete von MATLAB, doch eben nicht alle. Die komplette Liste der Beschränkungen findet sich auf der MATLAB Website. Mit dieser Methode können Sie "Software-unabhängige Bundles" von MetaTrader 5 und MATLAB erzeugen. Doch im Gegensatz zur MATLAB Engine, sollte dies nur von einem gut ausgebildeten Programmierer gemacht werden, der ein profundes Verständnis von Erstellung und Compilation hat.

Der MATLAB Compiler verlangt mindestens einen der folgenden C/C++ Compiler:

  • Lcc C (meist bei MATLAB dabei). Ist nur ein C Compiler.  
  • Borland C++, Versionen 5.3, 5.4, 5.5, 5.6.
  • Microsoft Visual C/C++, Versionen 6.0, 7.0, 7.1.

Der MATLAB Compiler 4 generiert, im Gegensatz zu seinen Vorgängern, nur den Interface-Code (Wrapper), d.h. er übersetzt keine m-Funktionen in binären oder C/C++ Code, sondern erstellt eine spezielle Datei auf Grundlage der Component Technology File (CTF) Technologie, die die Integrationen verschiedener Pakete, die zur Unterstützung der m-Funktionen nötig sind, umfasst. Der MATLAB Compiler verschlüsselt diese Datei zudem mit einem einmaligen (nicht wiederholten) 1024-Bit Schlüssel.

Betrachten wir uns nun die Arbeit des Algorithmus des MATLAB Compiler 4, denn wenn man dieses Thema nicht behandelt, kommt es zu vielen blöden Fehlern bei der späteren Erstellung,

  1. Abhängigkeiten-Analyse — an dieser Stelle Bitte alle Funktionen, MEX- und P-Dateien festlegen, von denen erstellte m-Funktionen abhängen.  
  2. Archiv anlegen - die CTF-Datei wird erzeugt, verschlüsselt und komprimiert.  
  3. Erzeugung des Objekt-Codes des Wrapper – jetzt werden alle Quellcodes erzeugt, die für folgende Komponente erforderlich sind:
    • C/C++ Interface-Code für m-Funktionen, in der Befehlszeile angegeben (NameFile_main.c).
    • Komponenten-Datei (NameFile_component.dat), die alle zur Ausführung von m-code erforderlichen Funktionen enthält (inkl. Verschlüsselungen und Verschlüsselungspfade, die in der CTF-Datei gespeichert sind).  
  4. C/C++ Übersetzung. Jetzt werden die C/C++ Quellcode-Dateien in Objektdateien erstellt.
  5. Verknüpfung. Die letzte Etappe des Projektaufbaus.

Wenn Sie nun mit dem Verhalten des MATLAB Compiler Algorithmus vertraut sind, müssen Sie noch mehr über die Schlüssel lernen, damit Sie auch einen Detailplan aller Handlungen haben, wenn Sie den Compiler verwenden (mcc):   

Schlüssel
Zweck
    ein Dateiname
 Die <filename> Datei dem Archiv hinzufügen, und festlegen, welche Dateien in das CTF-Archiv aufgenommen werden sollen
     l
 Makro zur Erzeugung einer Library an Funktionen
    N
 Alle Pfade löschen, außer dem minimal notwendigen Set an Directories
    p <directory>
 Gemäß des Verfahrens Pfad der Übersetzung hinzufügen. Braucht den -N Schlüssel.
    R -nojvm
 Option MCR abbrechen (MATLAB Component Runtime, vgl. MATLAB Hilfe)
    W
 Erzeugung der Funktions-Wrapper verwalten
    lib
 Initialisierung und Abschluss der Funktionen erzeugen
    main
 POSIX-Hülle der main() Funktion erzeugen
    T
 Output-Phase spezifizieren
    codegen
 Wrapper-Code für eigenständige Anwendung erzeugen
    compile:exe
 Der gleiche wie für codegen
    compile:lib
 Wrapper-Code für public DLL erzeugen
    link:exe
 Der gleiche wie für compile:exe plus Verknüpfung
    link:lib
 Der gleiche wie für compile:exe plus Verknüpfung

Tabelle 3 Schlüssel des MATLAB mcc Compilers (Version 4)

Tabelle 3 zeigt die grundlegenden Schlüssel, die bei der Lösung typischer Probleme nützlich sein könnten. Die MATLAB Befehle help mcc oder doc mcc führen Sie zu weiterer Hilfe.

Jetzt müssen wir den MATLAB-Linker kennen lernen, dessen hauptsächliche Keys unten dargestellt sind (mbuild):

 Schlüssel
Zweck
 -setup
 Im interaktiven Modus, Definition der Compiler-Optionen Datei, die standardmäßig bei zukünftigen Aufrufen von mbuild verwendet werden soll
 -g
 Programm mit Informationen zur Fehlersucher erstellen. Die Datei am Ende mit DEBUGFLAGS versehen.
 -O
 Optimierung des Objektcodes

Tabelle 4 Schlüssel des MATLAB mbuild-Linker (Version 4)

Tabelle 4 zeit die wichtigsten Schlüssel. Weitere Informationen finden Sie mit Hilfe der Befehle help mbuild oder doc mbuild.

2.3 C++ Standard Input/Output Library

Die Verwendung der Standard Input/Output Library stellt korrektes Kopieren von Daten sicher. Sie wird Sie vor "dummen" Fehlern bewahren, die in der Programmdesignphase entstehen werden (z.B.): viele noch unerfahrene Programmierer kopieren nur Zeiger des Memory-Blocks anstatt den gesamten Memory-Block). Aus der kompletten Input/Output Library interessiert uns nur eine Funktion:

void *pIn = memcpy(void *pIn, void *pOut, int nSizeByte) – diese Funktion kopiert (klont) eine Variable/Array von pOut in pIn mit der Byte-Größe nSizeByte, wobei:

  • void *pIn — der Zeiger auf das Array ist, wohin kopiert werden soll.  
  • void *pOut — der Zeiger auf das Array ist, woraus kopiert werden soll.  
  • int nSizeByte — die Größe der kopierten Daten bezeichnet, die nicht die Größe des pIn Arrays überschreiten sollte, denn sonst kommt es zu Fehlern beim Zugriff auf das Memory.  

3. Praxis

So, jetzt sind wir mit der Theorie durch und können uns mit der Umsetzung der MetaTrader 5 & MATLAB Interaktion beschäftigen.

Und wie Sie sich vielleicht schon gedacht haben, geht dies auf zwei Arten vonstatten: mit Hilfe der MATLAB Engine virtuellen Maschine und mit Hilfe der vom MATLAB Compiler angelegten Libraries. Als erstes konzentrieren wir uns auf die einfache, schnelle und vielseitig Möglichkeit der Interaktion — via der MATLAB Engine.

Bitte den folgenden Abschnitt von Anfang bis Ende lesen, da trotz der offensichtlichen Unterschiede der Interaktionsmethoden, beide dennoch auf einer Philosophie und einer bekannten Syntax und Sprachkonstruktion beruhen, und da außerdem das Erlernen von etwas Neuem anhand einfacher Beispiel meist leichter geht.

3.1 Entwicklung der Universellen Library der MetaTrader 5 & MATLAB Engine Interaktion

Zwar ist diese Interaktionsmethode nicht unbedingt elegant und schnell, doch stellt sie die zuverlässigste Variante dar und arbeitet mit dem gesamten MATLAB-Paket. Selbstverständlich sollten wir auch die Geschwindigkeit der endgültigen Modellentwicklung erwähnen. Der Kern der Entwicklung ist, einen universellen Library-Wrapper für die MetaTrader 4/5 & MATLAB Engine Interaktion zu schreiben. Danach kann das MetaTrader 4/5 Script/Indikator/Expert den MATLAB virtuellen Desktop verwalten. Und der gesamte mathematische Algorithmus kann als Strings im MQL-Programm abgelegt werden, sodass Sie damit Ihr geistiges Eigentum schützen können (weitere Einzelheiten dazu finden Sie im Beitrag "Schützt Euch, Entwickler!"). Er kann aber auch in separaten m-Funktionen oder P-Funktionen-Dateien im <MetaTrader 5>\MQL5\Libraries-Ordner gespeichert werden.  

Mögliche Anwendungsgebiete einer derartigen Interaktion:

  • Test oder Demonstration der "mathematischen Modelle/Ideen" ohne dazu komplexe Programme schreiben zu müssen (Schutz von geistigem Eigentum kann so wie im MQL-Programm arrangiert werden sowie auch mit Hilfe des MATLAB-Paket - mittels P-Funktionen).  
  • Schreiben komplexer mathematischer Modelle, die alle Features von MATLAB verwenden.
  • Geeignet für alle, die ihre Scripts/Indikatoren/Experts nicht verbreiten wollen.

Aber gehen wir nun weiter. Hoffentlich haben Sie alle die Abschnitte 1.1 Datentypen in MATLAB und MQL5, 1.2 Vergleich von MQL5 und MATLAB Datentypen, 2.1 MATLAB Engine API und MCR Funktionen und 2.3 C++ Standard Input/Output Library gelesen, denn auf diese Punkte gehen wir jetzt nicht noch einmal ein. Lesen Sie sich das folgende Blockschema aufmerksam durch, es stellt den Algorithmus der zukünftigen Library dar:  

Abb. 1 Blockschema des Library-Algorithmus

Abb. 1 Blockschema des Library-Algorithmus

Wie Abb. 1 zeigt, besteht die Library aus drei Hauptblöcken. Sehen wir uns ihre Zwecke an:

  • Der MQL5 Block: vorläufige Vorbereitung von gesendeten/empfangenen Daten:  
    • Umgekehrte Arrays.
    • Typ-Konvertierung.
    • Konvertierung der String-Verschlüsselung.
  • C/C++ block:
    • Konvertiert Arrays in eine mxArray Struktur.
    • Überträgt MATLAB Engine Befehle.
  • MATLAB Engine Block — Berechnungssystem.  

So, befassen wir uns jetzt mal mit den Algorithmen und beginnen gleich beim MQL5 Block. Dem aufmerksamen Leser ist vielleicht schon aufgefallen, dass es hier hauptsächlich um die Implementierung dessen geht, was theoretisch im Abschnitt Datentypen in MATLAB und MQL5 erklärt wurde. Wenn Sie diesen Abschnitt nicht gelesen haben, dann werden Sie kaum verstehen, warum das folgende nötig ist.

Der Algorithmus der mlInput <variable_type> Funktionen ist fast identisch. Erklären wir seine Arbeit unter Verwendung der mlInputDouble() Funktion, die die Eingabe der Variablen des double Typs in die MATLAB virtuelle Maschine übernimmt.

So sieht der Prototyp aus:

bool mlInputDouble(double &array[],int sizeArray, string NameArray), wobei:

  • array — Verweis auf Variable oder Array des double Typs.
  • sizeArray — Arraygröße (Anzahl der Elemente, nicht Bytes!). 
  • NameArray — String mit einmaligem Variablen-Namen für die MATLAB virtuelle Maschine (Name muss die MATLAB Voraussetzungen erfüllen).

Algorithmus:

  1. Konvertierung des NameArray Strings zu einem char Array mit Hilfe der StringToCharArray() Funktion.
  2. Prüfung der Indizierungsart mit Hilfe der ArrayIsSeries() Funktion. Handelt es sich um eine normale Indizierung — Wert an die mlxInputDouble() Funktion übertragen.
    ANDERNFALLS Indizierung eines Zeitreihen-Arrays:
    Array "umkehren" und den Wert an die mlxInputDouble() Funktion übertragen.
  3. Funktion beenden und den gelieferten Wert an die mlxInputDouble() Funktion übertragen.

Der Algorithmus der mlGet <variable_type> Funktionen ebenfalls fast identisch. Seine Arbeit sehen wir uns unter Verwendung der mlGetDouble() Funktion an, die Variablen des double Typs von der MATLAB virtuellen Maschine liefert.

Der Prototyp:

int mlGetDouble(double &array[],int sizeArray, string NameArray), wobei:

  • array — Verweis auf Variable oder Array des double Typs.
  • sizeArray — Arraygröße (Anzahl der Elemente, nicht Bytes!). 
  • NameArray — String mit einmaligen Variablen-Namen für die MATLAB virtuelle Maschine.

Algorithmus:

  1. Konvertierung des NameArray Strings zu einem char Array mit Hilfe der StringToCharArray() Funktion.   
  2. Mit Hilfe der mlxGetSizeOfName() Funktion die Arraygröße ermitteln.
    • IST sie GRÖSSER ALS NULL, mit Hilfe der ArrayResize() Funktion das Empfänger-Array mit entsprechender Größe zuweisen, Daten von mlxGetDouble() holen und Arraygröße liefern.
    • IST sie NULL entsteht ein Lieferungsfehler, d.h. null Wert.  

Das ist alles! Die mlGetInt() and mlGetLogical() Funktionen erzeugen eine "Schattenkonvertierung" derdouble ->; int/bool Typen. Zu diesem Zweck erzeugen diese Funktionen einen temporären Buffer in ihrem Korpus. Dies ist eine "erzwungene" Maßnahme, da MATLAB API leider die Erzeugung von mxArray Strukturen für andere Datentypen außer double zulässt. Das heißt jedoch nicht, dass MATLAB nur mit double Typen arbeitet.

Der C/C++ Block ist wesentlich leichter - er ist für die Datenübersetzung vom double Typ in diemxArray Struktur zuständig. Er wird mit Hilfe der mxCreateDoubleMatrix(), mxGetPr() und memcpy() Funktionen umgesetzt. Dann überträgt er, mit Hilfe der engPutVariable() Funktion, Daten an die MATLAB virtuelle Maschine, und holt sich mit Hilfe der engGetVariable() Funktion von dort Daten. Achten Sie genau auf Funktionen mit den Präfixen Int und Logical — wie im Blockschema gezeigt, da sie nicht direkt mit MATLAB interagieren, sondern dazu die mlxInputDouble/mlxGetDouble and mlxInputChar() Funktionen verwenden. Der Algorithmus ihres Verhaltens ist einfach: Aufruf der mlxInputDouble/mlxGetDouble Funktion — Eingabe-/Ausgabewerte als double(!) und dann den Befehl "Schatten" an MATLAB zur Konvertierung des Datentyps mit Hilfe der mlxInputChar() Funktion senden.

Und der MATLAB Engine Block ist noch leichter. Er liefert ausschließlich mathematische Funktionen. Sein verhalten hängt von Ihren Befehlen und Ihren m/p-Funktionen ab.  

Und wenn jetzt dann alle "Details" des Projekts klar sind, können wir beginnen es zu bauen.

Jedweder derartiger Aufbau beginnt stets mit der Erzeugung der Library — in unserem Fall ein C/C++ Block. Dazu erzeugt man in jedem ANSI Text-Editor (Notepad, Bred, usw.) eine Datei mit der Erweiterung DEF. Hierbei ist es wünschenswert, dass der Name dieser Datei lateinische Zeichen enthält, ohne Leerräume und Satzzeichen, ansonsten wird sich Ihr Compiler ausgiebig bei Ihnen "bedanken". Diese Datei stellt die Dauerhaftigkeit Ihrer Funktionen sicher. Sollte sie fehlen, erfindet der C/C++ Compiler seine eigenen "Fantasienamen", um die Funktionen exportieren zu können.

Die Datei enthält: LIBRARY — Kontrollwort, LibMlEngine — Name der Library und EXPORTS — zweites Kontrollwort, danach kommen die Funktionsnamen. Wie Sie ja vielleicht wissen, dürfen die Namen der Exportfunktionen keine Leerräume und Satzzeichen enthalten. Hier ist der Text der DllUnit.def Datei aus dem MATLABEngine.zip Archiv:  

LIBRARY LibMlEngine
EXPORTS
mlxClose
mlxInputChar
mlxInputDouble
mlxInputInt
mlxInputLogical
mlxGetDouble
mlxGetInt
mlxGetLogical
mlxGetSizeOfName
mlxOpen 

Also haben wir schon mal die erste DAtei des Projekts. Jetzt öffnen wir den Windows Explorer und gehen zum '<MATLAB>\Extern\include' Ordner. Dort kopieren wir die engine.h Datei (Kopfzeilen-Datei der MATLAB virtuellen Maschine) in den Ordner wo Ihr Projekt gebaut wird (wenn Sie das nicht tun, müssen Sie zum Zeitpunkt der Erstellung den Dateipfad manuell eingeben).

Erzeugen wir nun den C/C++ Block. Dieser Beitrag enthält nicht den gesamten Quellcode des Programms, da diese Datei in MATLABEngine.zip als DllUnit.cpp gefunden werden kann und sehr gut kommentiert ist. Beachten Sie, dass es besser ist ist, Funktionen mit Hilfe __stdcall convention — zu erzeugen, d.h. Parameter werden durch den Stack übertragen und die Funktion reinigt den Stack. Für Win32/64 API ist er Standard "native".

Sehen wir uns die DEklarierung der Funktion an:

extern "C" __declspec(dllexport) <variable_type> __stdcall Function(<type> <name>)
extern "C" __declspec(dllexport) — sagt dem C++ Compiler, dass es sich um eine externe Funktion handelt.  
  1. <variable_type> — Typ der gelieferten Variable, kann sein: void, bool, int, double, zusammengesetzte Typen (lösen nicht nur Dll aus, sondern rufen auch ein Programm auf) und Zeiger.
  2.  __stdcall — Deklarierung zur Übertragung von Parameter an die Funktion und zurück - das ist für Win32/64 API Standard.  
  3. Funcion — der Name Ihrer Funktion.  
  4. <type> <name> — Typ und Name der Eingabe-Variable; max. Zahl ist 64.

Dieses Thema wird detailliert im Beitrag Wie man Daten austauscht: Ein DLL für MQL5 in 10 Minuten beschrieben.

Bau des C/C++ Blocks: hierzu müssen Sie die Standard Input/Output-Library mit einschließen und Ihrem Projekt die folgenden Dateien hinzufügen (sind in Ihrem Compiler zu finden unter: Projekt->Projekt hinzufügen):

  1. DllUnit.def
  2. Im <MATLAB>\Extern\lib\<win32/64>\<compiler>\ Ordner, wobei
    <MATLAB> — der MATLAB Hauptordner ist.
    <win32/64> — entweder der win32 Ordner für das 32-Bit BS oder der win64 Ordner für das 64-Bit BS ist.
    <compiler> — der "borland" Ordner für die Borland C/C++ Version 5-6; der "microsoft" Ordner für Microsoft Visual C++:  
    • libeng.lib
    • libmx.lib

An dieser Stelle stellt sich häufig die Frage: "Ich habe unterschiedliche Compiler-Versionen oder keinen derartigen Compiler in meiner Liste! (Dass gar keine solchen Dateien vorhanden sind, kommt sehr selten vor)". Schauen wir uns an, wie wir eine public Library manuell erzeugen können. Das sehen wir uns sowohl in Visual C++ als auch in Borland C++ an:

  1. In FAR den <MATLAB>\Bin\<win32/64> Ordner öffnen, wobei
    <MATLAB> — der MATLAB Hauptordner ist.
    <win32/64> — entweder der win32 Ordner für das 32-Bit BS oder der win64 Ordner für das 64-Bit BS ist.  
  2. Für Borland C++ folgende Zeile eingeben: implib libeng.lib libeng.dll. Analog für libmx.dll.
  3. Für Visual C++ folgende Zeile eingeben: lib libeng.dll. Analog für libmx.dll.
  4. Bei jedem anderen Compiler: jeder Compiler egal welcher Programmiersprache muss das Dienstprogramm Library Manager haben. Meist ist es ein Konsolenprogramm: <compiler _folder>\bin\*lib*.exe.

Ach, ich habe vergessen Sie zu warnen: -Bitte niemals eine 64-Bit LIB für einen 32-Bit Compiler anlegen. Finden Sie zuerst heraus, ob es in der Compiler-Hilfe Suuport für die Arbeit mit 64-Bit gibt. Falls nein, suchen Sie entweder nach 32-Bit MATLAB DLL oder wählen Sie einen anderen C/C++ Compiler. Holen wir uns also eine Erstellung, nach der wir eine Library erhalten, die in den terminal_folder\MQL5\Libraries Ordner platziert werden sollte.

Fangen wir nun mit dem MQL-Block an. MetaEditor laufen lassen, "Neu" anklicken und so wie in den folgenden Abbildungen vorgehen:  

Abb. 2 MQL5 Assistent: Library anlegen

Abb. 2 MQL5 Assistent: Library anlegen

Abb. 3 MQL5 Assistent: Allgemeine Eigenschaften der Library

Abb. 3 MQL5 Assistent: Allgemeine Eigenschaften der Library

Sobald der MQL5 Assistent ein Template erstellt hat, können wir mit seiner Bearbeitung anfangen

1. Funktionsimport beschreiben:

//+------------------------------------------------------------------+
//| DECLARATION OF IMPORTED FUNCTIONS                                 |
//+------------------------------------------------------------------+
#import "LibMlEngine.dll"
void   mlxClose(void);                        //void – means: don't pass any parameters!
bool   mlxOpen(void);                         //void – means: don't pass and don't receive any parameters!
bool   mlxInputChar(char &CharArray[]);       //char& CharArray[] – means: pass a reference!
bool   mlxInputDouble(double &dArray[],
                      int sizeArray,
                      char &CharNameArray[]);
bool   mlxInputInt(double &dArray[],
                   int sizeArray,
                   char &CharNameArray[]);
bool   mlxInputLogical(double &dArray[],
                       int sizeArray,
                       char &CharNameArray[]);
int    mlxGetDouble(double &dArray[],
                    int sizeArray,
                    char &CharNameArray[]);
int    mlxGetInt(double &dArray[],
                 int sizeArray,
                 char &CharNameArray[]);
int    mlxGetLogical(double &dArray[],
                     int sizeArray,
                     char &CharNameArray[]);
int    mlxGetSizeOfName(char &CharNameArray[]);
#import    

Beachten Sie, dass Sie in MQL 5 "Zeiger" auf zweierlei Weise übertragen können:

  • void NameArray[] ;   // Diese Methode der Übertragung von einem Array erlaubt nur Daten zu lesen. Wenn Sie jedoch diesen Verweis bei "seinen Inhalt bearbeiten" anwenden, wird Ihnen ein Fehler beim Zugriff auf das Memory gemeldet (im besten Fall behebt MetaTrader den Fehler im SEH-Frame still und heimlich für Sie, doch haben wir NOCH KEINEN SEH-Frame GESCHRIEBEN, also können wir den Grund des Fehlers auch übergehen).
  • void& NameArray[] ; // Diese Methode der Übertragung erlaubt das Lesen und Bearbeiten von Array-Inhalten, doch Sie müssen die Array-Größe beibehalten.

Wenn die Funktion keine Parameter akzeptiert oder überträgt, dann Bitte immer den Typ unwirksam angeben.

2. Wir beschreiben hier nicht alle Funktionen des MQL-Blocks, denn der MatlabEngine.mq5 Quellcode ist in MATLABEngine.zip enthalten.

Daher behandeln wir als nächstes die Details der Deklarierung und Definition der externen Funktionen in MQL5:

bool mlInputChar(string array)export
{
//... body of function
}

In diesem Beispiel sieht man, dass die Deklarierung und Definition der Funktion kombiniert sind. Also deklarieren wir also eine Funktion namens mlInputChar() al extern(Export), die den Wert des bool Typs liefert und den array String als Parameter akzeptiert. Und jetzt erstellen...

Da wir jetzt den letzten Library-Block abgeschlossen und erstellt haben, können wir das Ganze nun unter echten Bedingungen testen.

Und dazu schreiben wir ein einfaches Test-Script (oder nehmen es aus MATLABEngine.zip, Datei: TestMLEngine.mq5).

Der Scriptcode ist einfach und sehr gut kommentiert:

#property copyright "2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru"
#property version   "1.00"
#import "MatlabEngine.ex5"
bool mlOpen(void);
void mlClose(void);
bool mlInputChar(string array);
bool mlInputDouble(double &array[],
                   int sizeArray,
                   string NameArray);
bool mlInputInt(int &array[],
                int sizeArray,
                string NameArray);
int mlGetDouble(double &array[],
                string NameArray);
int mlGetInt(int &array[],
             string NameArray);
bool mlInputLogical(bool &array[],
                    int sizeArray,
                    string NameArray);
int mlGetLogical(bool &array[],
                 string NameArray);
int mlGetSizeOfName(string strName);
#import
void OnStart()
  {
// Dynamic buffers for MATLAB output
   double dTestOut[];
   int    nTestOut[];
   bool   bTestOut[];
// Variables for MATLAB input
   double dTestIn[] = {   1,     2,    3,     4};
   int    nTestIn[] = {   9,    10,   11,    12};
   bool   bTestIn[] = {true, false, true, false};
   int nSize=0;
// Variables names and command line
   string strComm="clc; clear all;"; // command line - clear screen and variables
   string strA     = "A";            // variable A
   string strB     = "B";            // variable B
   string strC     = "C";            // variable C
/*
   ** 1. RUNNING DLL
   */
   if(mlOpen()==true)
     {
      printf("MATLAB has been loaded");
     }
   else
     {
      printf("Matlab ERROR! Load error.");
      mlClose();
      return;
     }
/*
   ** 2. PASSING THE COMMAND LINE
   */
   if(mlInputChar(strComm)==true)
     {
      printf("Command line has been passed into MATLAB");
     }
   else printf("ERROR! Passing the command line error");
/*
   ** 3. PASSING VARIABLE OF THE DOUBLE TYPE
   */
   if(mlInputDouble(dTestIn,ArraySize(dTestIn),strA)==true)
     {
      printf("Variable of the double type has been passed into MATLAB");
     }
   else printf("ERROR! When passing string of the double type");
/*
   ** 4. GETTING VARIABLE OF THE DOUBLE TYPE
   */
   if((nSize=mlGetDouble(dTestOut,strA))>0)
     {
      int ind=0;
      printf("Variable A of the double type has been got into MATLAB, with size = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("A = %g",dTestOut[ind]);
        }
     }
   else printf("ERROR! Variable of the double type double hasn't ben got");
/*
   ** 5. PASSING VARIABLE OF THE INT TYPE
   */
   if(mlInputInt(nTestIn,ArraySize(nTestIn),strB)==true)
     {
      printf("Variable of the int type has been passed into MATLAB");
     }
   else printf("ERROR! When passing string of the int type");
/*
   ** 6. GETTING VARIABLE OF THE INT TYPE
   */
   if((nSize=mlGetInt(nTestOut,strB))>0)
     {
      int ind=0;
      printf("Variable B of the int type has been got into MATLAB, with size = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("B = %i",nTestOut[ind]);
        }
     }
   else printf("ERROR! Variable of the int type double hasn't ben got");
/*
   ** 7. PASSING VARIABLE OF THE BOOL TYPE
   */
   if(mlInputLogical(bTestIn,ArraySize(bTestIn),strC)==true)
     {
      printf("Variable of the bool type has been passed into MATLAB");
     }
   else printf("ERROR! When passing string of the bool type");
/*
   ** 8. GETTING VARIABLE OF THE BOOL TYPE
   */
   if((nSize=mlGetLogical(bTestOut,strC))>0)
     {
      int ind=0;
      printf("Variable C of the bool type has been got into MATLAB, with size = %i",nSize);
      for(ind=0; ind<nSize; ind++)
        {
         printf("C = %i",bTestOut[ind]);
        }
     }
   else printf("ERROR! Variable of the bool type double hasn't ben got");
/*
   ** 9. ENDING WORK
   */
   mlClose();
  }

Wie man an den Script ersieht, geben wir Werte ein und erhalten dann auch wieder Werte. Im Gegensatz zu MetaTrader 4, wo wir in der Designphase die Buffergröße wissen mussten, ist dies jedoch in MetaTrader 5 nicht mehr nötig, da wir mit dynamischen Buffern arbeiten.

Und jetzt, da Sie die MATLAB virtuelle Maschine endgültig verstanden haben, können Sie mit der Verwendung des in die MATLAB-Umgebung eingebauten DLL beginnen.

3.2 Technische Richtlinien des Baus/Verwendung von DLL, die der MATLAB Compiler 4 erzeugt hat

Im vorigen Abschnitt haben Sie gelernt, wie man eine Library zur universellen Interaktion mit dem MATLAB-Paket anlegt. Diese Methode hat jedoch einen Nachteil - man braucht dazu das MATLAB-Paket des End-Anwenders. Dies führt zu einigen Schwierigkeiten bei der Verbreitung des fertigen Softwareprodukts. Und genau deswegen hat das MATLAB mathematische Paket einen eingebauten Compiler, mit dem man "eigenständige Anwendungen erzeugen kann", die vom MATLAB-Paket unabhängig sind. Sehen wir uns das mal an.

Betrachten wir uns als Beispiel den einfachen Indikator - gleitender Durchschnitt (SMA). Ihn rüsten wir durch Hinzufügen des Neural Network Filters (GRNN) leicht auf, sodass wir "weißes Rauschen" (zufällige Ausbrüche) glätten können. Wir nennen den neuen Indikator NeoSMA und den Filter GRNNFilter.  

So erhalten wir zwei m-Funktionen, aus denen wir einen DLL erzeugen wollen, der vom MetaTrader 5 aufgerufen werden kann.

Hier sollten Sie sich daran erinnern, dass MetaTrader 5 nach DLLs in folgenden Ordner sucht:

  • <terminal_dir>\MQL5\Libraries  
  • <terminal_dir>  
  • Aktueller Ordner
  • Systemordner <windows_dir>\SYSTEM32;  
  • <windows_dir>  
  • In den in der Systemumgebungs-Variable PFAD gelisteten Directories.

Daher platzieren in eines dieser Directories zwei m-Funktionen (NeoSMA.m und GRNNFilter.m), wo wir DLL bauen werden, . Ich betone noch einmal diese Positionierung - Sie müssen das tun, das erfolgt nicht zufällig. Der aufmerksame Leser kennt bereits das MATLAB Compiler-Feature - der Pfad wird bei der Erstellung bewahrt (vgl. Abschnitt 2.2 "MATLAB Compiler 4").

  Bevor Sie mit der Erstellung des Projekts beginnen, müssen Sie den Compiler noch konfigurieren. Das geschieht mit folgenden Schritten:   

  1. Sie geben in der MATLAB Befehlszeile ein: mbuild -setup
  2. 'y' drücken zur Bestätigung des Findens von C/C++ kompatiblen Compiler, die auf Ihrem System installiert sind.
  3. Den Standard Lcc-win32 C Compiler wählen.
  4. 'y' drücken zur Bestätigung des ausgewählten Compilers.

Abb. 4 Projekt erstellen

Abb. 4 Projekt erstellen


Jetzt können wir uns der Erstellung von m-Funktionen zuwenden.

Dazu müssen wir folgendes eingeben:

mcc -N -W lib:NeoSMA -T link:lib  NeoSMA.m GRNNFilter.m

Erklärung der Schlüssel:

-N —  Überspringen aller unnötigen Pfade
-W lib:NeoSMA —  sagt dem Compiler, dass NeoSMA der Library-Name ist
-T link:lib —  sagt dem Compiler eine public Library anzulegen, indem
NeoSMA.m und GRNNFilter.m — die Namen der m-Funktionen — verknüpft werden.

Mal schauen was uns der Compiler erstellt hat:

  • mccExcludedFiles.log  —  Protokolldatei mit allen Compiler-Aktionen
  • NeoSMA.c  —  C-Version der Library (enthält den С-Code des Wrappers)  
  • NeoSMA.ctf  —  CTF-Datei (vgl. Abschnitt 2.2 MATLAB Compiler 4)  
  • NeoSMA.h  —  Kopfzeilen-Datei (enthält die Deklarierungen der Libraries, Funktionen und Konstanten)  
  • NeoSMA.obj  —  Objektdatei (Quelldatei mit Maschine und Pseudo-Code)  
  • NeoSMA.exports  —  Namen der exportierten Funktionen  
  • NeoSMA.dll  —  Dll für weitere Verknüpfung  
  • NeoSMA.lib  —  Dll zur Verwendung in C/C++ Projekten  
  • NeoSMA_mcc_component_data.c  —  C-Version der Komponente (nötig für die Konformität mit der CTF-DAtei, enthält Pfade, usw.)  
  • NeoSMA_mcc_component_data.obj  —  Objektversion der Komponente (Quelldatei mit Maschine und Pseudo-Code);

Also arbeiten wir mit dem DLL, genauer gesagt, seiner internen Struktur. Sie besteht aus (nur grundlegende Funktionen):

  1. Hauptfunktion jedes DLL - BOOL WINAPI DllMain(), die (laut der Microsoft-Spezifikation) in DLL auftretende Ereignisse bearbeitet: Laden des DLL in das Adressfeld des Prozesses, Erzeugung eines neuen Stroms, Löschen des Stroms und Dll aus dem Memory holen.  
  2. Dienstfunktionen für die DLL Initialisierung/De-Initialisierung: BOOL <NameLib>Initialize(void)/void <NameLib>Terminate(void) — nötig zum Starten/Entladen der Mathematischen Arbeitsumgebung vor und nach der Verwendung von Library-Funktionen.
  3. Exportierte m-Funktionen – void mlf<NameMfile>(int <number_of_return_values>, mxArray **<return_values>, mxArray *<input_values>, ...), wobei:
    • <number_of_return_values> — die Anzahl der gelieferten Variablen ist (nicht zu verwechseln mit Arraygröße, usw.).
    • mxArray **<return_values> — die Adresse der mxArray-Struktur ist, in die die Arbeitsergebnisse der m-Funktion geliefert werden.
    • mxArray *<input_values> — der Zeiger auf die mxArray-Struktur der m-Funktion Eingabe-Variable ist.
     

Sie sehen hier, dass exportierte m-Funktionen Adressen und Zeiger auf eine mxArray-Struktur enthalten und Sie daher diese Funktionen nicht direkt von MetaTrader 5 aufrufen können, da er diesen Datentyp nicht versteht. Wir beschreiben hier keine mxArray-Struktur im MetaTrader 5, da die MATLAB-Entwickler nicht garantieren können, dass sie sich nicht im Laufe der Zeit ändern können, selbst bei derselben Produktversion. Daher müssen Sie einen einfachen DLL-Adapter schreiben.

Sein Blockschema ist unten abgebildet:

Abb. 5 Blockschema des DLL-Adapters

Abb. 5 Blockschema des DLL-Adapters

Es ähnelt der rechten Seiten des DLL für die MATLAB Engine sehr, daher zerlegen wir seinen Algorithmus nicht und gehen gleich zum Code weiter. Dazu müssen zwei kleine Dateien in Ihrem C/C++ Compiler erzeugt werden:  

nSMA.cpp (aus DllMatlab.zip):  

#include <stdio.h>
#include <windows.h>
/* Include MCR header file and library header file */
#include "mclmcr.h"
#include "NEOSMA.h"
/*---------------------------------------------------------------------------
** DLL Global Functions (external)
*/
extern "C" __declspec(dllexport) bool __stdcall IsStartSMA(void);
extern "C" __declspec(dllexport) bool __stdcall nSMA(double *pY,  int  nSizeY,
                                                     double *pIn, int nSizeIn,
                                                     double   dN, double dAd);
/*---------------------------------------------------------------------------
** Global Variables
*/
mxArray *TempY;
mxArray *TempIn;
mxArray *TempN;
mxArray *TempAd;
bool bIsNeoStart;
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    switch(reason)
    {
        case DLL_PROCESS_ATTACH:
         bIsNeoStart = false;
         TempY  = 0;   //Nullify pointers to buffers
         TempN  = 0;
         TempIn = 0;
         TempAd = 0;
         break;
        case DLL_PROCESS_DETACH:
         NEOSMATerminate();
         //Delete old data before exiting from Dll
         if(TempY  != NULL) mxDestroyArray(TempY);
         if(TempN  != NULL) mxDestroyArray(TempN);
         if(TempIn != NULL) mxDestroyArray(TempIn);
         if(TempAd != NULL) mxDestroyArray(TempAd);
         mclTerminateApplication();
    }
    return 1;
}
//---------------------------------------------------------------------------
bool __stdcall IsStartSMA(void)
{
 if(bIsNeoStart == false)
 {
  if(!mclInitializeApplication(NULL,0) )
  {
   MessageBoxA(NULL, (LPSTR)"Can't start MATLAB MCR!",
               (LPSTR) "MATLAB DLL: ERROR!", MB_OK|MB_ICONSTOP);
   return false;
  }else
   {
    bIsNeoStart = NEOSMAInitialize();
   };
 };
 return bIsNeoStart;
}
//---------------------------------------------------------------------------
bool __stdcall nSMA(double *pY, int nSizeY, double *pIn, int nSizeIn, double dN, double dAd)
{
   /*
   ** Create buffers
   */
   if(TempN == NULL){ TempN = mxCreateDoubleMatrix(1, 1, mxREAL);}
   else
   {
     mxDestroyArray(TempN);
     TempN= mxCreateDoubleMatrix(1, 1, mxREAL);
   };
   if(TempIn == NULL){ TempIn = mxCreateDoubleMatrix(1, nSizeIn, mxREAL);}
   else
   {
     mxDestroyArray(TempIn);
     TempIn= mxCreateDoubleMatrix(1, nSizeIn, mxREAL);
   };
   if(TempAd == NULL){ TempAd = mxCreateDoubleMatrix(1, 1, mxREAL);}
   else
   {
     mxDestroyArray(TempAd);
     TempAd= mxCreateDoubleMatrix(1, 1, mxREAL);
   };
   /*
   ** Creating data for processing
   */
   memcpy((char *)mxGetPr(TempIn), (char *) pIn, (nSizeIn)*8);
   memcpy((char *)mxGetPr(TempN), (char *) &dN, 8);
   memcpy((char *)mxGetPr(TempAd), (char *) &dAd, 8);
   /*
   ** Send and receive a response from the m-function
   */
   if(mlfNeoSMA(1, (mxArray **)TempY, (mxArray *)TempIn, (mxArray *)TempN
      , (mxArray *)TempAd) == false) return false;
   /*
   ** Return calculated vector from the m-function and clear buffers
   */
   memcpy((char *) pY, (char *)mxGetPr(TempY), (nSizeY)*8);
   mxDestroyArray((mxArray *)TempY);  TempY  = 0;
   mxDestroyArray((mxArray *)TempN);  TempN  = 0;
   mxDestroyArray((mxArray *)TempIn); TempIn = 0;
   mxDestroyArray((mxArray *)TempAd); TempAd = 0;
   return true;
}

nSMA.def (aus DllMatlab.zip):

LIBRARY nnSMA
EXPORTS
IsStartSMA
nSMA


Das Projekt in Ihrem C/C++ Compiler bauen: dazu müssen Sie die Standard Input/Output-Library mit einschließen und Ihrem Projekt die folgenden Dateien hinzufügen (in Ihrem Compiler: Projekt->Projekt hinzufügen):

  1. nSMA.def
  2. In <MATLAB>\Extern\lib\<win32/64>\<compiler>\ Ordner, wobei:
    <MATLAB> — der MATLAB Hauptordner ist.
    <win32/64> — entweder der win32 Ordner für das 32-Bit BS oder der win64 Ordner für das 64-Bit BS ist.
    <compiler> — der "borland" Ordner für die Borland C/C++ Version 5-6 ist oder der "microsoft" Ordner für Microsoft Visual C++ (ich habe Dateien für Version 6):  
    • libmx.lib
    • mclmcr.lib
  3. NeoSMA.lib — manuell erzeugen (vgl. dazu den Abschnitt 3.1 Entwicklung der Universellen Library der MetaTrader 5 & MATLAB Engine Interaktion).  

Was ich Ihnen in diesem Abschnitt hier als letztes noch sagen möchte betrifft die Dateien, die notwendig sind, wenn Sie das Projekt auf einen anderen Computer umziehen, auf dem kein MATLAB installiert ist.

Dies ist die Liste der Dateien und Pfade für den Zielrechner:

  • MCRInstaller.exe                    jeder Ordner (MCR-Installer)
  • extractCTF.exe                      jeder Ordner (für MCR-Installer)
  • MCRRegCOMComponent.exe  jeder Ordner (für MCR-Installer)
  • unzip.exe                              jeder Ordner (für MCR-Installer)
  • NeoSMA.dll                           <terminal_dir>\MQL5\Libraries
  • NeoSMA.ctf                           <terminal_dir>\MQL5\Libraries
  • nnSMA.dll                             <terminal_dir>\MQL5\Libraries

Viele fortgeschrittene Programmierer haben sich wahrscheinlich schon gedacht, dass die Verwendung eines Installationsprogramms (SETUP) ratsam ist. Von diesen Programmen gibt es viele im Internet, einschließlich kostenlose.

Jetzt müssen wir diesen DLL in MetaTrader 5 noch testen. Dazu schreiben wir ein einfaches Script (TestDllMatlab.mq5 aus der DllMatlab.zip Datei):

#property copyright "2010, MetaQuotes Software Corp."
#property link      "nav_soft@mail.ru"
#property version   "1.00"
#import "nnSMA.dll"
bool  IsStartSMA(void);
bool  nSMA(double &pY[],
           int nSizeY,
           double &pIn[],
           int nSizeIn,
           double dN,
           double dAd);
#import
datetime    Time[];    // dynamic array of time coordinates
double      Price[];   // dynamic array of price
double      dNeoSma[]; // dynamic array of price
void OnStart()
  {
   int ind=0;
// run Dll
   if(IsStartSMA()==true)
     {
      //--- create and fill arrays
      CopyTime(Symbol(),0,0,301,Time);   // time array + 1
      ArraySetAsSeries(Time,true);       // get the time chart
      CopyOpen(Symbol(),0,0,300,Price);  // price array
      ArraySetAsSeries(Price,true);      // get the open prices
      ArrayResize(dNeoSma,300,0);        // reserve space for function response
                                         // get data
      if(nSMA(dNeoSma,300,Price,300,1,2)==false) return;
      // specify array orientation
      ArraySetAsSeries(dNeoSma,true);
      // plot data on chart
      for(ind=0; ind<ArraySize(dNeoSma);ind++)
        {
         DrawPoint(IntegerToString(ind,5,'-'),Time[ind],dNeoSma[ind]);
        }
     }
  }
//+------------------------------------------------------------------+
void DrawPoint(string NamePoint,datetime x,double y)
  {  // 100% ready. Plot data on chart. Drawing using arrows.
// Main properties of chart object
   ObjectCreate(0,NamePoint,OBJ_ARROW,0,0,0);
   ObjectSetInteger(0, NamePoint, OBJPROP_TIME, x);        // time coordinate x
   ObjectSetDouble(0, NamePoint, OBJPROP_PRICE, y);        // price coordinate y
// Additional properties of chart object
   ObjectSetInteger(0, NamePoint, OBJPROP_WIDTH, 0);       // line width
   ObjectSetInteger(0, NamePoint, OBJPROP_ARROWCODE, 173); // arrow type
   ObjectSetInteger(0, NamePoint, OBJPROP_COLOR, Red);     // arrow color
  }
//+------------------------------------------------------------------+

Fazit

Jetzt können Sie also eine universelle Library für die MetaTrader 5 & MATLAB Interaktion anlegen und wissen auch, wie man in die MATLAB-Umgebung eingebaute DLL verknüpft. Dennoch müssen noch weitere Interfaces der MetaTrader 5 & MATLAB Interaktion beschrieben werden, was jedoch den Umfang dieses Beitrags derzeit sprengen würde. Das Thema dieses Beitrags ist in allen Einzelheiten behandelt. Ich habe hier die effektivsten Wege der Interaktion gewählt, die eben keine spezielle Art von "Adaptern" brauchen. Sie können jedoch auch gerne "einen Weg verfolgen", wie z.B. die .NET-Technologie - Wie man Notierungen aus MetaTrader 5 in .NET-Anwendungen mit Hilfe der WCF-Dienste exportiert.

Für viele Leser stellt sich bestimmt die Frage: mit welcher Methode soll ich arbeiten? Die Antwort darauf ist einfach: mit beiden, denn während des Designs/Fehlersuche des mathematischen Modells kommt es auf Geschwindigkeit nicht an. Doch Sie werden die volle Leistung von MATLAB ohne "spezielle Produktionskosten" für die Programmierung brauchen. Und hier ist die MATLAB Engine natürlich eine große Hilfe Sobald das mathematische Modell jedoch frei von Fehlern und einsatzbereit ist, dann brauchen Sie Geschwindigkeit fürs Multitasking (die Arbeit des Indikators und/oder Handelssystems auf unterschiedlichen Kurscharts). Und jetzt brauchen Sie auf jeden Fall ein, in die MATLAB Umgebung eingebautes DLL.

Doch all das zwingt Sie nicht, sich auch strikt daran zu halten. Jeder von Ihnen beantwortet sich diese Frage schon selbst und verlässt sich dabei primär auf das Verhältnis "Programmierkosten" vs. Projektumfang (Anzahl der Anwender des Indikators und/oder Handelssystems). Es macht überhaupt keinen Sinn, nur für einen oder zwei Anwender Dll in der MATLAB-Umgebung zu erzeugen (da ist's leichter gleich MATLAB auf zwei Computern zu installieren).  

Viele Leser, die sich mit MATLAB (noch) nicht auskennen, fragen sich vielleicht auch: Was soll das Ganze? MQL5 hat ja bereits mathematische Funktionen! Die Antwort liegt hier in der einfachen Tatsache begründet, dass Sie mit MATLAB mühelos Ihre mathematischen Überlegungen implementieren können. Und das hier ist nur eine kleine Auswahl aller Möglichkeiten:  

  • dynamischer "Fuzzy Logic"-Algorithmus im Indikator und/oder mechanischen Handelssystem  
  • dynamischer genetischer Algorithmus im mechanischen Handelssystem (dynamischer Strategie-Tester)
  • dynamischer neuronaler Netzwerk-Algorithmus im Indikator und/oder mechanischen Handelssystem  
  • Dreidimensionale Indikatoren
  • Simulation nicht-linearer Verwaltungssysteme

Also es liegt bei Ihnen. Vergessen Sie jedoch eines nicht: "Mathematik war schon immer die Königin der Wissenschaften" — und das MATLAB-Paket ist Ihr wissenschaftlicher Taschenrechner.

Bibliographie

  1. MATLAB eingebaute Hilfe.
  2. MQL5 eingebaute Hilfe.
  3. Jeffrey Richter. Programmierungs-Anwendungen für Microsoft Windows.

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

Beigefügte Dateien |
dllmatlab_en.zip (955.7 KB)
matlabengine_en.zip (670.57 KB)
Erzeugung eines Indikators mit graphischen Kontrolloptionen Erzeugung eines Indikators mit graphischen Kontrolloptionen
All diejenigen, die sich mit Stimmungen auf dem Markt auskennen, kennen den MACD Indikator (seiner voller Name lautet Gleitender Durchschnitt Konvergenz/Divergenz) -das leistungsstarke Tool zur Analyse von Kursbewegungen, das von Händlers seit dem ersten Auftauchen von Computer-gestützten Analysemethoden verwendet wird. Dieser Beitrag beschäftigt sich mit möglichen Veränderungen des MACD und ihrer Implementierung in einen Indikator mit der Möglichkeit, zwischen den Veränderungen graphisch hin- und her zu wechseln.
Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger
Die komplexen Codes bestehen aus einer Reihe einfacher Codes. Kennt man diese, dann sieht alles gleich nicht mehr so kompliziert aus. In diesem Beitrag beschäftigen wir uns mit der Erzeugung eines Indikators mit mehreren Indikator-Buffern. Als Beispiel wird hierzu der Aroon-Indikator detailliert analysiert und zwei unterschiedliche Versionen dieses Codes präsentiert.
Verarbeitung von Ereignissen in MQL5: Unmittelbare Änderung des Zeitraums für den gleitenden Durchschnitt Verarbeitung von Ereignissen in MQL5: Unmittelbare Änderung des Zeitraums für den gleitenden Durchschnitt
Angenommen, dass ein MA (Gleitender Durchschnitt) Indikator mit Zeitraum 13 auf ein Chart angewandt wird. Und wir den Zeitraum auf 20 ändern wollen, wir dazu aber nicht in das Dialogfenster Indikator-Eigenschaften gehen und die Zahl von 13 zu 20 ändern wollen, da wir einfach auf diese Aktionen mit der Maus und der Tastatur keine Lust mehr haben. Und wir wollen vor allem nicht den Indikator-Code öffnen und dort was ändern. Wir wollen einfach nur eine Taste drücken - und zwar "Bild nach oben" neben dem Zahlenfeld auf der Tastatur. In diesem Beitrag sage ich Ihnen wie das geht.
MQL für Anfänger: Wie man Objektklassen entwirft und baut MQL für Anfänger: Wie man Objektklassen entwirft und baut
Durch Erstellung eines Beispielprogramms von visuellen Designs, zeigen wir, wie man in MQL5 Klassen entwirft und baut. Dieser Beitrag richtet sich an Programmierer im Anfängerstadium, die auf MT5 Anwendung arbeiten. Wir schlagen hier eine einfache und leicht zu verstehende Technologie zur Erzeugung von Klassen vor, ohne dass man dazu tief in den Theorie des Objekt-orientieren Progammierens einsteigen muss.