English Русский 中文 Español Português
preview
Marktsimulation: Erste Schritte mit SQL in MQL5 (I)

Marktsimulation: Erste Schritte mit SQL in MQL5 (I)

MetaTrader 5 — Tester |
17 0
Daniel Jose
Daniel Jose

Einleitung

Hallo zusammen und herzlich willkommen zu einem weiteren Artikel über die Erstellung eines Replikations-/Simulationssystems.

Im vorherigen Artikel „Marktsimulation (Teil 24): Erste Schritte mit SQL (VII)“ haben wir meiner Meinung nach die wichtigsten Grundlagen gelegt, die Sie benötigen, um auch ohne SQL-Erfahrung zumindest zu verstehen, worum es im Folgenden geht. Der Grund dafür ist, dass wir nun in eine neue Phase der Entwicklung des Replikations-/Simulationssystems eintreten, und hier sind SQL-Kenntnisse von entscheidender Bedeutung, damit Sie diesem und den folgenden Artikeln folgen können.

Machen Sie sich keine Sorgen, wenn Sie gerade erst angefangen haben, SQL zu lernen, und sich ausschließlich auf das in den vorherigen Artikeln erworbene Wissen stützen. Wenn dieses Material richtig angewendet und verstanden wird, reicht es bereits aus, um viele der Aufgaben zu lösen, die für uns wirklich wichtig sind. In dieser ersten Phase ist es meine eigentliche Absicht, dass wir bei SQLite bleiben, da es bereits in MetaTrader 5 integriert ist, was die Nutzung erheblich vereinfacht, ohne dass wir auf DLLs oder gar Sockets zurückgreifen müssen.

Wenn Sie also SQLite bereits in Ihren MQL5-Programmen verwenden, wird dieser Artikel Ihnen nichts Neues bieten. Wenn Sie jedoch gerade erst Ihre ersten Schritte in dieser Welt machen, in der der Umgang mit Datenbanken sehr ernst genommen werden muss, und Sie noch nicht wissen, wie Sie dies mit MQL5 umsetzen können, dann sollten Sie es sich am besten bequem auf Ihrem Stuhl machen und diesen Artikel sorgfältig durchlesen. Hier werden wir uns damit befassen, wie man mit der Sprache MQL5 die Arbeit mit einer SQL-Datenbank aufnimmt. Wir werden jedoch nicht irgendeine Datenbank verwenden, sondern SQLite. Und wie Sie bereits in den vorherigen Artikeln gesehen haben, können wir innerhalb dieses Systems eine Menge erreichen. All das mit minimalem Aufwand in MQL5.


Erstellen einer SQL-Datenbank mit MQL5

Also gut. Als Erstes müssen wir eine Datenbank anlegen. Wie wir bereits in früheren Artikeln gesehen haben, ist das recht einfach und unkompliziert. Sie können direkt im MetaEditor, über die Befehlszeile oder mit einem speziellen Programm zur Bearbeitung von SQL-Skripten arbeiten. Aber ich möchte, dass Sie verstehen, dass wir es nicht unbedingt genau so machen müssen. Dies lässt sich direkt programmatisch umsetzen, wobei wir für unseren Zweck die eine oder andere Sprache verwenden können.

Wenn man danach sucht, findet man viele Personen, die denselben Vorgang auf unterschiedliche Weise und in verschiedenen Sprachen erklären. Wenn man jedoch genauer hinschaut, wird man feststellen, dass im Grunde alles auf SQL hinausläuft. Und genau hier wird es erst richtig interessant. Wenn Sie diese Prinzipien verstehen, können Sie beispielsweise in Java geschriebenen Code für die Implementierung in MQL5 anpassen. Der Grund dafür ist, dass letztendlich der SQL-Code die Daten tatsächlich verarbeitet und mit den Datensätzen arbeitet. Java bietet lediglich eine benutzerfreundlichere Oberfläche für eine bestimmte Nutzergruppe.

Diesen Ansatz werden Sie auch in VBA wiederfinden, wo wir Excel nutzen, um Daten darzustellen, die direkt über SQL abgerufen werden. Deshalb ist es so wichtig zu verstehen, wie SQL-Code wirklich funktioniert. Letztendlich werden wir SQL verwenden. Die Programmiersprache, in die SQL eingebettet ist, stellt lediglich die von SQL bereitgestellten Daten dar.

Wenn Sie verstehen, wie das alles wirklich funktioniert, wird Ihnen das später sehr helfen. Beginnen wir also mit dem grundlegendsten Prinzip: dem Erstellen einer SQLite-Datenbankdatei mit MQL5. Da es unser Ziel ist, dies so verständlich wie möglich zu erklären, werden wir die einfachste Methode anwenden. Mit anderen Worten: Wir werden ein MQL5-Skript verwenden. Das liegt daran, dass ein Skript leichter zu verstehen und einfacher zu testen ist. Das erste Codefragment, das wir verwenden werden, ist unten zu sehen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Basic script for SQL database written in MQL5"
04. #property version   "1.00"
05. #property script_show_inputs
06. //+------------------------------------------------------------------+
07. input string user01 = "DataBase01";        //FileName
08. //+------------------------------------------------------------------+
09. void OnStart()
10. {
11.     string szFileName = user01 + ".sqlite";
12.     int handleDB;
13.     
14.     if ((handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE)) == INVALID_HANDLE)
15.     {
16.         Print("Unable to create or open the file ", szFileName);
17.         return;
18.     }
19.     DatabaseClose(handleDB);
20.     Print("Database file saved successfully..");
21. }
22. //+------------------------------------------------------------------+

Code in MQL5

Dies ist der einfachste Code zum Öffnen oder Erstellen einer Datenbankdatei. All dies erfolgt mithilfe von MQL5 und SQLite. Beachten Sie, dass er sehr leicht zu verstehen ist. In Zeile 07 lassen wir den Benutzer des Skripts den Namen der Datenbank angeben, die angelegt oder geöffnet werden soll. Beachten Sie bitte, dass wir die zu verwendende Dateiendung hier noch nicht festlegen; das werden wir später tun. Die verwendete Dateiendung ist in Zeile 11 angegeben. Bitte beachten Sie, dass wir hier den vom Skriptbenutzer angegebenen Namen verwenden und ihn mit einem weiteren String verketten, bei dem es sich in diesem Fall um die Dateiendung handelt.

Es ist wichtig zu beachten, dass wir jede beliebige Dateiendung verwenden können, aber ich würde Ihnen raten, eine zu wählen, die den Dateityp angibt. Für eine SQL-Datenbank ist die gängigste Dateiendung .DB oder, im Fall von SQLite, .sqlite, da wir SQLite-spezifische Elemente verwenden können. Dies wurde bereits in früheren Artikeln erläutert. Bei Unklarheiten sollten Sie diese Artikel und die dort genannten Quellen konsultieren, um das Thema besser zu verstehen.

Da wir also mit Dateien arbeiten werden, benötigen wir ein Handle. Dieses Handle wird in Zeile 12 gezeigt. In Zeile 14 verwenden wir die Funktion DatabaseOpen, die Teil von MQL5 ist, um die Datenbankdatei zu öffnen oder bei Bedarf anzulegen. An dieser Stelle sollte ein wichtiges Detail beachtet werden. Eine Datenbankdatei kann an drei verschiedenen Speicherorten angelegt werden. All dies erfolgt mithilfe derselben Funktion DatabaseOpen. Die Speicherorte der Daten sind wie folgt: Der erste befindet sich im RAM, was bedeutet, dass die Datenbank nach dem Schließen nicht mehr existiert. Das ist in bestimmten Situationen sehr nützlich, aber in unserem Fall werden wir es nicht verwenden, zumindest vorerst nicht. Der zweite

und der dritte Speicherort hängt von den an die Funktion DatabaseOpen übergebenen Argumenten ab. Bitte beachten Sie, dass wir im gezeigten Code die Argumente DATABASE_OPEN_CREATE und DATABASE_OPEN_READWRITE verwenden. Infolgedessen wird die Datenbankdatei irgendwo im Ordner „MQL5\Files“ angelegt. Ich sage „irgendwo“, weil der vom Benutzer angegebene Name einen gültigen Verzeichnisnamen enthalten kann. In diesem Fall müssen wir in das vom Benutzer angegebene Verzeichnis wechseln. Auf jeden Fall befindet sich dieses Verzeichnis gerade aufgrund der angegebenen Argumente im Ordner „MQL5\Files“.

Dieser Speicherort kann jedoch abweichen, wenn wir zusätzlich zu den im Code gezeigten Argumenten auch das Argument DATABASE_OPEN_COMMON hinzufügen. Dadurch ändert sich der Ordner von „MQL5\Files“ zu „COMMON\Files“. „Aber wo befindet sich der Ordner ‚COMMON‘, und wie greife ich darauf zu?“ Solche Fragen kommen recht häufig vor. Also, lieber Leser, wenn Sie nicht wissen, wo sich dieser Ordner befindet, navigieren Sie auf Ihrer lokalen Festplatte zu folgendem Pfad:

C:\Users\<UserName>\AppData\Roaming\MetaQuotes\Terminal

An diesem Speicherort finden Sie den Ordner „COMMON“ und darin den Ordner „Files“. Vergessen Sie nicht, <UserName> durch den Namen des bei Windows angemeldeten Benutzers zu ersetzen.

Sollte die Erstellung der angegebenen Datei oder der Zugriff darauf verweigert werden, wird die Meldung aus Zeile 16 im MetaTrader 5-Terminal ausgegeben. Unmittelbar danach, in Zeile 17, wird das Skript beendet. Wenn jedoch alles reibungslos verläuft, schließen wir in Zeile 19 die Datenbankdatei und geben in Zeile 20 im MetaTrader 5-Terminal eine Meldung aus, dass alles erfolgreich abgeschlossen wurde. Dadurch können Sie die Datei im MetaEditor öffnen und sehen dann eine Ansicht, die der auf dem folgenden Bild gezeigten ähnelt:

An dieser Stelle könnten wir im MetaEditor mit SQL arbeiten. In unserem Fall werden wir es jedoch aus MQL5 heraus verwenden. Dennoch können Sie den MetaEditor oder sogar die Programme aus den vorherigen Artikeln, in denen SQL erläutert wurde, verwenden, um nachzuvollziehen, was mit MQL5 gemacht wird. Zu Beginn der Auseinandersetzung mit der Verwendung von SQLite in MQL5 ist es wichtig, den Fortschritt der Vorgänge überwachen zu können. Grundkenntnisse in SQL sind hierfür sehr hilfreich. Nutzen Sie daher die Begleitmaterialien aus den vorherigen Artikeln, um den Lernprozess noch weiter zu beschleunigen. Was wir hier tun werden, ist ziemlich interessant.

Ausgezeichnet. Da wir nun bereits wissen, wie man eine SQL-Datenbankdatei erstellt oder öffnet, können wir zum nächsten Schritt übergehen. Aber lassen Sie mich zunächst etwas klarstellen, das möglicherweise Zweifel aufkommen lassen könnte. Wenn wir davon sprechen, dass wir eine Datei öffnen, auf Daten zugreifen und Datensätze per SQL hinzufügen oder ändern werden, sollten Sie bitte bedenken, dass wir dabei SQLite verwenden werden. Der Grund dafür ist, dass die Ausführung derselben Aktionen, die wir hier durchführen werden, bei Verwendung einer anderen SQL-Implementierung – wie beispielsweise MySQL oder SQL Server – etwas anders abläuft. Bringen Sie die Dinge also nicht durcheinander.

SQL ist eine Sprache; es gibt jedoch verschiedene Implementierungen, die unterschiedliche Möglichkeiten bieten. Hier werden wir ausschließlich mit SQLite arbeiten, zumindest in der Anfangsphase der Implementierung. Möglicherweise werden wir unsere Meinung in Zukunft noch ändern, aber sollte dies der Fall sein, werden wir erläutern, wie man von SQLite zu einer anderen SQL-Implementierung wechselt. Sie sollten jedoch keine Scheu davor haben, SQL zu lernen, denn die Sprache SQL bleibt immer dieselbe, unabhängig von der Anwendung oder SQL-Implementierung, in der sie zum Einsatz kommt.


Die ersten Tabellen erstellen

Genau wie bei der Erläuterung der SQL-Befehle werden wir hier ganz ähnlich vorgehen. Dies ist erforderlich, damit der Übergang von der Programmierung in reinem SQL zu SQL, das in anderen Code eingebettet ist, reibungslos verläuft. In diesem Fall ist der Code, der diese Befehle entgegennimmt, in MQL5 geschrieben. Glücklicherweise haben die MQL5-Entwickler hervorragende Arbeit geleistet und dafür gesorgt, dass alles recht einfach zu verstehen und zu bedienen ist. Daher müssen wir im Grunde nur lernen, sechs der in MQL5 verfügbaren Funktionen für die Arbeit mit Datenbanken zu nutzen. „Aber werden dann die anderen Funktionen überflüssig?“ Nein, das habe ich nicht gesagt. Ich habe gesagt, dass wir lernen müssen, sechs Funktionen zu nutzen. Und wenn wir wissen, wie man mit diesen sechs Funktionen umgeht, und uns ein wenig mit SQL auskennen, können wir jede damit zusammenhängende Situation problemlos bewältigen. Zumindest gilt dies für die Arbeit mit SQLite, das in MQL5 enthalten ist.

Wenn wir SQLite über eine DLL nutzen wollen, wird die Vorgehensweise etwas anders sein. Das liegt daran, dass wir in diesem Fall die Aufrufe direkt über die in der DLL-Datei enthaltene SQLite-Bibliothek ausführen werden. Wenn das also genau auf Ihren Fall zutrifft, empfehle ich Ihnen, die Dokumentation zur Verwendung von SQLite über eine DLL durchzulesen, denn dort ist alles etwas anders als das, was wir hier betrachten werden.

Schauen wir uns nun an, welche Funktionen wir verwenden lernen müssen. Wir haben bereits zwei davon vorgestellt, nämlich DatabaseOpen und DatabaseClose. Wir werden aber auch die Funktionen DatabaseExecute, DatabasePrepare, DatabaseReadBind und DatabaseFinalize benötigen. Natürlich ist es in vielen Situationen sehr hilfreich, wenn man weiß, wie man die anderen Funktionen nutzt. Aber mit diesen sechs Funktionen können wir nun ganz einfach fast alles erstellen – vorausgesetzt natürlich, wir verfügen über gute Grundkenntnisse in der SQL-Programmierung.

Dank der Inhalte der vorherigen Artikel werden Sie zumindest bereits in der Lage sein, einige recht interessante Dinge zu tun. Außerdem können Sie damit Dinge auf eine Weise umsetzen, die vielen anderen nicht möglich wäre. Aber wie versprochen werden wir alles Schritt für Schritt angehen, damit jeder folgen und verstehen kann, was gerade passiert.

Also gut. Wie Sie vielleicht schon in früheren Artikeln bemerkt haben, hat alles, was wir tun, in irgendeiner Weise mit Tabellen zu tun. Beginnen wir also mit den Grundlagen dieses Themas. Unser neues Skript ist somit im Folgenden aufgeführt.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Basic script for SQL database written in MQL5"
04. #property version   "1.00"
05. #property script_show_inputs
06. //+------------------------------------------------------------------+
07. input string user01 = "DataBase01";        //FileName
08. //+------------------------------------------------------------------+
09. void OnStart()
10. {
11.     string szFileName = user01 + ".sqlite",
12.              szRequestSQL;
13.     int handleDB;
14.     
15.     if ((handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE)) == INVALID_HANDLE)
16.     {
17.         Print("Unable to create or open the file ", szFileName);
18.         return;
19.     }
20.     szRequestSQL = "CREATE TABLE IF NOT EXISTS tb_Symbol(id PRIMARY KEY, symbol NOT NULL UNIQUE);";
21.     if (!DatabaseExecute(handleDB, szRequestSQL))
22.     {
23.         Print("Request execution failed.");
24.         DatabaseClose(handleDB);
25.         return;
26.     }
27.     DatabaseClose(handleDB);
28.     Print("Database file saved successfully.");
29. }
30. //+------------------------------------------------------------------+

Code in MQL5

Wenn dieses Skript in MetaTrader 5 ausgeführt wird, erhalten wir folgendes Ergebnis:

„Wow, das ist toll. Aber wie wurde das gemacht?“ Ganz einfach. Wir haben nur das verwendet, was wir in den vorherigen Artikeln behandelt haben, in denen wir SQL erklärt haben. Aber hier haben wir alles mit MQL5 gemacht. Wie? Ausgehend von dem Skript, das wir im vorherigen Abschnitt gesehen haben, fügen wir dem Code nur ein paar Zeilen hinzu. Mit einer Einschränkung: Dieser Code weist noch nicht die richtige Struktur auf. Hier möchte ich Ihnen zeigen, wie wir dasselbe tun können, was wir zuvor gesehen haben. Daher fügen wir Zeile 12 hinzu, bei der es sich um einen String handelt, der den SQL-Befehl aufnimmt, den wir ausführen möchten. In Zeile 20 legen wir fest, um welchen Befehl es sich handelt. In diesem Fall weisen wir SQL an, eine Tabelle namens „tb_Symbol“ anzulegen, falls diese in der Datenbank noch nicht vorhanden ist.

Unmittelbar danach weisen wir SQLite mithilfe der Funktion DatabaseExecute an, den SQL-Befehl auszuführen. Sollte dieser Vorgang fehlschlagen, wird die Meldung in Zeile 23 im MetaTrader 5-Terminal angezeigt. Da die Datenbank geöffnet ist, müssen wir sie schließen, was in Zeile 24 geschieht. In Zeile 25 beenden wir die Ausführung des Skripts in MetaTrader 5.

Beachten Sie nun bitte einen Punkt. Bei der Verwendung von SQLite, das in MetaTrader 5 enthalten ist, wird jeder Befehl, den wir zur Ausführung an SQLite senden möchten, über DatabaseExecute übermittelt. Dies ist jedoch nur dann geeignet, wenn wir SQL-Befehle absetzen möchten. Bei der Ausführung von Abfragen müssen wir auch andere Funktionen verwenden. Beachten Sie, wie alles zusammenpasst und dass wir entwickeln können, was wir benötigen.

Bevor wir uns also mit Abfragen befassen, wollen wir dieses MQL5-Skript etwas universeller gestalten, damit es sich besser für die von uns erstellte Datenbank eignet. Da die Struktur geändert werden kann, können wir dasselbe Skript in eine Klasse umwandeln. Auch wenn dies zum jetzigen Zeitpunkt vielleicht einfach erscheint, wird es doch eine große Hilfe sein, da wir den Code dadurch wiederverwendbarer und somit sicherer und stabiler gestalten können. Anschließend wandeln wir denselben Code, den wir zuvor gesehen haben, in den unten gezeigten Code um, nur dass wir jetzt Klassen verwenden:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Basic script for SQL database written in MQL5"
04. #property version   "1.00"
05. #property script_show_inputs
06. //+------------------------------------------------------------------+
07. input string user01 = "DataBase01";         //FileName
08. //+------------------------------------------------------------------+
09. class C_DB_SQLite
10. {
11.     private :
12.             int     m_handleDB;
13.     public  :
14. //+------------------------------------------------------------------+
15.             C_DB_SQLite(const string szFileName)
16.                     :m_handleDB(INVALID_HANDLE)
17.                     {
18.                             if ((m_handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE)) == INVALID_HANDLE)
19.                             {
20.                                     Print("Unable to create or open the file ", szFileName);
21.                                     return;
22.                             }
23.                     }
24. //+------------------------------------------------------------------+
25.             ~C_DB_SQLite()
26.                     {
27.                             DatabaseClose(m_handleDB);
28.                             Print("Closing Database...");
29.                     }
30. //+------------------------------------------------------------------+
31.             bool Command(const string szRequestSQL)
32.                     {
33.                             bool ret = DatabaseExecute(m_handleDB, szRequestSQL);                           
34.                             Print("Request execution: ", (ret ? "Success" : "Failed"), "...");
35.                             return ret;
36.                     }
37. //+------------------------------------------------------------------+
38. };
39. //+------------------------------------------------------------------+
40. void OnStart()
41. {
42.     C_DB_SQLite *DB;
43.     
44.     DB = new C_DB_SQLite(user01 + ".sqlite");
45.     
46.     (*DB).Command("CREATE TABLE IF NOT EXISTS tb_Symbol(id PRIMARY KEY, symbol NOT NULL UNIQUE);");
47.     
48.     delete DB;      
49. }
50. //+------------------------------------------------------------------+

Code in MQL5

Bitte beachten Sie, dass wir nun über eine wesentlich komplexere Struktur für die Arbeit mit der Datenbank verfügen. Das liegt daran, dass wir – ganz gleich, was wir tun – immer dann, wenn wir Befehle an die Datenbank senden, diese an die richtige Datenbank senden, unabhängig davon, ob eine Datenbank oder Dutzende von Datenbanken geöffnet sind. Der Befehl wird immer an die richtige Datenbank gesendet. Das liegt daran, dass die Funktionen nun innerhalb der in den Zeilen 9 bis 38 erstellten Klasse verwendet werden, und das ist erst der Anfang. Dennoch profitieren wir bereits von der gesamten Sicherheit, die die Klasse bietet.

Beispielsweise können wir ein Handle nicht verwenden, wenn es nicht initialisiert wurde. Und da die Initialisierung über den Konstruktor der Klasse erfolgt, können wir sicher sein, dass wir stets ein gültiges Handle verwenden. Dies ist in Zeile 33 zu sehen, wo wir das Handle verwenden, um einen Befehl an SQL zu senden.

Nun möchte ich, dass Sie dem folgenden Punkt besondere Aufmerksamkeit schenken: Viele halten es vielleicht für übertrieben, eine Klasse zu verwenden oder gar zu erstellen, um die Kommunikation mit SQLite zu kapseln, aber lassen Sie uns ein wenig darüber nachdenken – vielleicht ändert das Ihre Meinung. Sie könnten denken: „Warum sollte ich eine Klasse erstellen, nur um das in MetaTrader 5 integrierte SQLite zu nutzen? Es ist viel einfacher, die MQL5-Funktionen direkt zu verwenden. Zumal ich SQL in meinen Programmen nicht intensiv nutzen werde.“ Aus dieser Perspektive muss ich Ihnen zustimmen, aber betrachten wir die Sache doch einmal in einem etwas breiteren Kontext. Mit MQL5 können wir einfach und unkompliziert auf das in MetaTrader 5 integrierte SQLite zugreifen. Gut. Aber wir sollten uns nicht darauf beschränken.

Wenn wir eine ganze Reihe von Anwendungen oder sogar ein System entwickeln, das das in MetaTrader 5 integrierte SQLite nutzt, und dabei ausschließlich MQL5-Funktionen verwenden, werden wir irgendwann darauf beschränkt sein, ausschließlich das SQLite von MetaTrader 5 zu nutzen. Wir werden unseren eigenen SQLite-Build in einer DLL-Datei nicht verwenden können. Und das Schlimmste daran ist: Sollten wir uns irgendwann entscheiden, eine andere SQL-Implementierung zu verwenden – sei es MySQL, SQL Server oder eine andere –, werden wir viel Arbeit damit haben, unseren gesamten Code neu zu schreiben, um die erforderlichen Abhängigkeiten zu berücksichtigen. Das liegt daran, dass MySQL und SQL Server, die wir bisher nur erwähnt haben, kein Dateisystem verwenden, auf das mit MQL5 zugegriffen werden kann. Wir können sogar MQL5 verwenden, doch dafür müssen wir ein anderes Tool nutzen, dessen Anwendung wir in dieser Serie bereits vorgestellt haben. Es geht um Sockets.

Mit anderen Worten: Um auf Datenbanken zuzugreifen, die eine Client-Server-Architektur verwenden, müssen wir Sockets nutzen. Wenn unsere Implementierung von Anfang an mit einem Klassensystem konzipiert wurde, um Implementierungsdetails zu verbergen, müssen wir lediglich die Klasse ändern, indem wir sie erweitern oder anpassen, damit sie die erforderliche Unterstützung für die Kommunikation über Sockets bereitstellen kann. Dies vereinfacht den gesamten Entwicklungsprozess erheblich. Denn wenn wir die SQL-Implementierung ändern müssen, indem wir das in MetaTrader 5 verwendete SQLite durch unser eigenes, in eine DLL kompiliertes SQLite ersetzen, ist dies problemlos möglich.

Das Gleiche gilt, wenn wir die Implementierung ändern müssen. Beim Umstieg von dateibasiertem SQLite auf Client-Server-SQL werden wir keine größeren Probleme haben. Es reicht aus, lediglich die Funktionsweise der Klasse anzupassen, und der restliche Code wird von diesen Änderungen profitieren. So einfach ist das. Unterschätzen Sie daher nicht die Vorteile, eine Anwendung mit Blick darauf zu entwickeln, dass sich ihre Funktionsweise in Zukunft ändern könnte. Denken Sie daran: Technologien ändern sich im Laufe der Zeit. Eine Implementierung zu erstellen, die fest an eine bestimmte Technologie gebunden ist, ist nicht die beste Wahl.


Einige Aspekte verstehen

Bevor wir fortfahren, möchte ich Ihnen etwas zeigen, denn es ist sehr wichtig, dass Sie es verstehen. Machen wir Folgendes. In den vorherigen Artikeln haben wir eine Beziehung zwischen Tabellen hergestellt, um eine relationale Datenbank aufzubauen. Wir haben erläutert, wie dies umgesetzt werden kann, sowie die Vor- und Nachteile dieser Art der Strukturierung einer Datenbank nach dem relationalen Modell dargelegt. Wenn wir jedoch mit einer großen Anzahl von SQL-Befehlen über die in MetaTrader 5 integrierte SQLite-Engine arbeiten, müssen wir bestimmte Vorsichtsmaßnahmen treffen. Oder, genauer gesagt, wir müssen einen Weg finden, dies sicherer zu gestalten.

Was wir hier zeigen werden, wird uns dazu zwingen, im nächsten Artikel eine Reihe von Entscheidungen zu treffen. Studieren Sie den folgenden MQL5-Code sorgfältig:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Basic script for SQL database written in MQL5"
04. #property version   "1.00"
05. #property script_show_inputs
06. //+------------------------------------------------------------------+
07. input string user01 = "DataBase01";        //FileName
08. //+------------------------------------------------------------------+
09. class C_DB_SQLite
10. {
11.     private    :
12.         int    m_handleDB;
13.     public    :
14. //+------------------------------------------------------------------+
15.         C_DB_SQLite(const string szFileName)
16.             :m_handleDB(INVALID_HANDLE)
17.             {
18.                 if ((m_handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE)) == INVALID_HANDLE)
19.                 {
20.                     Print("Unable to create or open the file ", szFileName);
21.                     return;
22.                 }
23.             }
24. //+------------------------------------------------------------------+
25.         ~C_DB_SQLite()
26.             {
27.                 DatabaseClose(m_handleDB);
28.                 Print("Closing Database...");
29.             }
30. //+------------------------------------------------------------------+
31.         bool Command(const string szRequestSQL)
32.             {
33.                 bool ret = DatabaseExecute(m_handleDB, szRequestSQL);                
34.                 Print("Request execution: ", (ret ? "Success" : "Failed"), "...");
35.                 return ret;
36.             }
37. //+------------------------------------------------------------------+
38. };
39. //+------------------------------------------------------------------+
40. void OnStart()
41. {
42.     C_DB_SQLite *DB;
43.     
44.     DB = new C_DB_SQLite(user01 + ".sqlite");
45.     
46.     (*DB).Command("PRAGMA FOREIGN_KEYS = ON;");    
47.     (*DB).Command("CREATE TABLE IF NOT EXISTS tb_Symbols"
48.                   "("
49.                     "id PRIMARY KEY,"
50.                     "symbol NOT NULL UNIQUE"
51.                   ");");
52.     (*DB).Command("CREATE TABLE IF NOT EXISTS tb_Quotes"
53.                   "("
54.                     "of_day NOT NULL,"
55.                     "price NOT NULL,"
56.                     "fk_id NOT NULL,"
57.                     "FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id)"
58.                   ");");    
59.     delete DB;
60. }
61. //+------------------------------------------------------------------+

Code in MQL5

Achten Sie darauf, wie die Befehle im MQL5-Code angeordnet sind. Dies ist für die Ausführung von SQL-Befehlen erforderlich. Es kommt oft vor, dass uns beim Schreiben so vieler Dinge irgendwann der eine oder andere Fehler unterläuft. Schauen Sie sich nun das folgende Skript an:

01. PRAGMA FOREIGN_KEYS = ON;
02. 
03. CREATE TABLE IF NOT EXISTS tb_Symbols
04. (
05.     id PRIMARY KEY,
06.     symbol NOT NULL UNIQUE
07. );
08. 
09. CREATE TABLE IF NOT EXISTS tb_Quotes
10. (
11.     of_day NOT NULL,
12.     price NOT NULL,
13.     fk_id NOT NULL,
14.     FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id)
15. );

Code in SQLite

Vergleichen Sie dieses Skript mit dem vorherigen. Beachten Sie, dass das letztere Skript zwar reines SQL verwendet und dessen Syntax bereits beim Schreiben analysiert werden kann, wir diese Möglichkeit jedoch nicht haben, wenn wir Strings direkt in den MQL5-Code einfügen. Dies ist der Mechanismus, über den MQL5 mit der in MetaTrader 5 integrierten SQLite-Engine interagiert. Das heißt: Wenn wir beim Schreiben von SQL-Code innerhalb eines Strings im MQL5-Code einen Tippfehler machen, kann SQL unseren Code nicht korrekt ausführen. Infolgedessen könnten wir MQL5 oder sogar MetaTrader 5 die Schuld geben, was jedoch ein großer Fehler wäre. Es ist jedoch zu beachten, dass wir in beiden Versionen des Codes eine Datenbank mit miteinander in Beziehung stehenden Tabellen erstellen. Und sollten wir den Tabellennamen „tb_Symbols“ versehentlich einmal als „tb_Symbol“ schreiben, wird das Ergebnis völlig anders ausfallen als in der folgenden Abbildung dargestellt:

Bitte beachten Sie: Wenn wir versuchen, im MetaEditor dieselbe Struktur wie in der vorherigen Abbildung anzuzeigen, wird dies nicht gelingen. Darüber haben wir bereits in einem anderen Artikel gesprochen. Falls Sie nicht verstehen, was hier vor sich geht, empfehle ich Ihnen, die Artikel zu lesen, in denen wir SQL erläutert und erklärt haben, wie Sie die Programme erhalten, die Sie benötigen, um das SQL-System effektiver zu nutzen.

Aber genau das möchte ich hier hervorheben. Wenn man SQL-Code, der in einem String steht, kann jedoch Fehler enthalten, die nicht leicht zu erkennen sind. Wenn wir jedoch einen Editor verwenden, der SQL-Code analysieren kann, lassen sich diese Fehler viel leichter finden, wie im Folgenden zu sehen ist:

Es ist zu beachten, dass es wesentlich einfacher ist, einen Fehler in einem eigenständigen SQL-Skript zu entdecken. Wie ich bereits erwähnt habe, kommt es nicht selten vor, dass man als Programmierer Tippfehler macht. Der Einsatz der richtigen Werkzeuge kann also viel Zeit sparen, die man sonst damit verbringen würde, herauszufinden, warum ein bestimmter Codeabschnitt funktioniert oder nicht funktioniert. In den meisten Fällen handelt es sich bei dem Fehler gerade um einen Tippfehler, der bei der Eingabe eines Befehls unterlaufen ist.


Abschließende Gedanken

Im heutigen Artikel haben wir damit begonnen, die Verwendung von SQL im MQL5-Code zu behandeln. Wir haben uns angesehen, wie eine Datenbank angelegt werden kann. Oder, genauer gesagt, wie man mithilfe der in MQL5 integrierten Funktionen eine SQLite-Datenbankdatei erstellt. Außerdem haben wir uns angesehen, wie man eine Tabelle erstellt und wie man anschließend mithilfe eines Primärschlüssels und eines Fremdschlüssels eine Beziehung zwischen Tabellen herstellt. All dies erfolgt wiederum mithilfe von MQL5. Wir haben gesehen, wie einfach es ist, Code zu erstellen, der später auf andere SQL-Implementierungen übertragen werden kann, indem man eine Klasse verwendet, die dabei hilft, die zugrunde liegende Implementierung zu verbergen.

Und vor allem: Wir haben festgestellt, dass bei der Verwendung von SQL an verschiedenen Stellen Probleme auftreten können. Das liegt daran, dass SQL-Code innerhalb des MQL5-Codes in einen String-Wert eingebettet wird. Allein diese Tatsache macht es recht wahrscheinlich, dass etwas schiefgehen kann, sodass die Ausführung von SQL-Code innerhalb von MQL5 aufgrund eines Tippfehlers problematisch wird. Wir können und werden jedoch einen Mechanismus entwickeln, der uns nicht nur mehr Freiheit verschafft, sondern es uns auch ermöglicht, diese Probleme zu vermeiden, denn es gibt Editoren, die darauf ausgelegt sind, SQL-Skripte leichter lesbar zu machen.

Im nächsten Artikel werden wir also einen Mechanismus entwickeln, der eine solche Portabilität erleichtert. Viele, die bereits etwas Erfahrung haben, können sich wahrscheinlich schon denken, worum es hier geht. Falls das auf Sie nicht zutrifft, machen Sie sich keine Sorgen; im nächsten Artikel werden wir uns damit befassen, wie es geht. Und wir werden unsere Arbeit im SQL-Bereich fortsetzen, nur dass wir nun MQL5 als Ausführungsgrundlage verwenden werden. Dies ist erforderlich, damit MetaTrader 5 unserem Replikations-/Simulationssystem ermöglicht, Datenbanken bei seiner Arbeit zu nutzen. Lernen Sie also weiter, und bis zum nächsten Artikel, in dem alles noch interessanter wird.

Datei Beschreibung
Experts\Expert Advisor.mq5
Veranschaulicht die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist Mouse Study erforderlich).
Indicators\Chart Trade.mq5 Erstellt ein Fenster zur Konfiguration der zu sendenden Order (für die Interaktion ist Mouse Study erforderlich).
Indicators\Market Replay.mq5 Erstellt Steuerelemente für die Interaktion mit dem Replikations-/Simulationsdienst (für die Interaktion ist Mouse Study erforderlich).
Indicators\Mouse Study.mq5 Stellt die Interaktion zwischen grafischen Steuerelementen und dem Benutzer bereit (sowohl für das Replikations-/Simulationssystem als auch für den realen Markt erforderlich).
Services\Market Replay.mq5 Erstellt und verwaltet den Dienst für die Marktreplikation/-simulation (die Hauptdatei des gesamten Systems).
Code VS C++\Servidor.cpp Erstellt und verwaltet einen in C++ entwickelten Server-Socket (MiniChat-Version).
Code in Python\Server.py Erstellt und verwaltet einen Socket in Python für die Kommunikation zwischen MetaTrader 5 und Excel.
Indicators\Mini Chat.mq5 Ermöglicht die Implementierung eines Mini-Chats über einen Indikator (für den Betrieb ist ein Server erforderlich).
Experts\Mini Chat.mq5 Ermöglicht die Implementierung eines Mini-Chats mithilfe eines Expert Advisors (für den Betrieb ist ein Server erforderlich).
Scripts\SQLite.mq5 Veranschaulicht die Verwendung eines SQL-Skripts über MQL5.
Files\Script 01.sql Zeigt die Erstellung einer einfachen Tabelle mit einem Fremdschlüssel.
Files\Script 02.sql Zeigt das Hinzufügen von Werten in die Tabelle.

Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/13062

Beigefügte Dateien |
Anexo.zip (571.71 KB)
Marktsimulation: Erste Schritte mit SQL in MQL5 (V) Marktsimulation: Erste Schritte mit SQL in MQL5 (V)
Im vorherigen Artikel habe ich gezeigt, wie vorzugehen ist, um einen Abfragemechanismus hinzuzufügen. Dies war erforderlich, damit Sie innerhalb des MQL5-Codes SQL in vollem Umfang nutzen und Ergebnisse mithilfe einer SQL-SELECT-Abfrage abrufen können. Aber es gibt noch eine letzte Funktion, die wir implementieren müssen. Dabei handelt es sich um die Funktion DatabaseReadBind. Da ein korrektes Verständnis dieser Funktion eine etwas ausführlichere Erläuterung erfordert, wurde beschlossen, dieses Thema nicht im vorherigen Artikel, sondern im heutigen Artikel zu behandeln. Da das Thema also recht umfangreich ist, gehen wir direkt zum nächsten Abschnitt über.
Vom Anfänger zum Fortgeschrittenen: Objekte (I) Vom Anfänger zum Fortgeschrittenen: Objekte (I)
In diesem Artikel werden wir uns zunächst damit befassen, wie man direkt im Chart mit Objekten arbeitet. Dies geschieht mithilfe von Code, der speziell zu Demonstrationszwecken entwickelt wurde. Die Arbeit mit Objekten ist sehr interessant und kann viel Spaß machen. Da dies unsere erste Begegnung mit diesem Thema sein wird, fangen wir mit etwas ganz Einfachem an.
Neuronale Netze im Trading: Zerlegung strukturierter Komponenten (SCNN) Neuronale Netze im Trading: Zerlegung strukturierter Komponenten (SCNN)
Wir laden Sie ein, das innovative SCNN-Framework kennenzulernen, das die Zeitreihenanalyse auf eine neue Ebene hebt, indem es die Daten klar in langfristige, saisonale, kurzfristige und Residualkomponenten unterteilt. Dieser Ansatz verbessert die Prognosegenauigkeit erheblich, da er es dem Modell ermöglicht, sich an komplexe und sich verändernde Marktdynamiken anzupassen.
Neuronale Netze im Handel: Ein End-to-End-Modell zur multivariaten Zeitreihenprognose (Fazit) Neuronale Netze im Handel: Ein End-to-End-Modell zur multivariaten Zeitreihenprognose (Fazit)
Wir freuen uns, Ihnen den letzten Teil unserer Serie über GinAR vorstellen zu dürfen – ein neuronales Netzwerk-Framework zur Zeitreihenprognose. In diesem Artikel analysieren wir die Ergebnisse der Modelltests mit neuen Daten und bewerten seine Robustheit unter realen Marktbedingungen.