English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Wie man auf die MySQL-Datenbank von MQL5 (MQL4) aus zugreift

Wie man auf die MySQL-Datenbank von MQL5 (MQL4) aus zugreift

MetaTrader 5Integration | 2 Juni 2016, 09:57
3 032 0
Eugeniy Lugovoy
Eugeniy Lugovoy

Einleitung

Das Problem der Interaktion von MQL mit Datenbanken ist nicht neu, doch ist es immer noch ungelöst. Die Arbeit mit Datenbanken kann die Möglichkeiten von MetaTrader erheblich verbessern: Speicherung und Analyse der Kurshistorie, Kopieren von Handel von einer Handelsplattform auf eine andere, Lieferung von Notierungen/Handel in Echtzeit, aufwendige analytische Berechnungen auf Serverseite und/oder Verwendung eines Terminplans, Monitoring und remote Kontrolle von Konten mittels Web-Technologien.

Auf jeden Fall hat es viele Versuche gegeben, von der Kombination von MQL und MySQL zu profitieren. Einige dieser Lösungen sind in der CodeBase vorhanden.

So ist z.B. "MySQL-Wrapper - Library für MetaTrader 4" das Projekt, von dem aus viele Programmierer ihre eigenen Entwicklungen mit weiteren Ergänzungen beginnen. Meiner Meinung nach liegt einer der Nachteile dieser Lösung in der Zuweisung von speziellen Arrays für das Lesen von Daten von der Datenbank.

Ein weiteres Projekt,"MySQL-Logger 1 - EA für MetaTrader 4" ist extrem speziell, denn es verwendet zum Zugriff auf die Standard-Library libmysql.dll keinen Wrapper. Daher funktioniert es in MetaTrader4 Bauart 600+ nicht, da die char Zeichentypen durch wchar_tersetzt wurden und der Einsatz des int-Typs anstelle des TMYSQL Struktur-Zeigers zu Speicherlecks im Projekt führt (der zugewiesene Speicher kann nicht kontrolliert/freigemacht werden).

Ein weiteres interessantes Projekt ist "EAX_Mysql - MySQL-Library - Library für MetaTrader 5" - eine ziemlich gute Implementierung. Die Liste der vom Autor aufgeführten Nachteile jedoch spricht für einige Einschränkungen bei der Verwendung.

Jeder, der in seinen MQL-Projekten mit Datenbanken wird arbeiten müssen, hat daher zwei Optionen: entweder seine eigene Lösung zu entwickeln und jeden einzelnen Teil zu kennen oder irgendeine Lösung Dritter zu verwenden/anzupassen, sich mit ihrer Anwendung vertraut zu machen und alle Mängel selbst herauszufinden, die sein Projekt behindern würden.

Ich selbst war auch auf den Einsatz von Datenbanken angewiesen und habe die beiden Optionen bei der Entwicklung eines ziemlich komplexen Handelsroboters selbst erfahren müssen. Nachdem ich bestehende Projekte durchforstet und eine große Menge von Lösungen studiert habe, wurde mir klar, dass keine der gefundenen Implementierungen mir dabei helfen konnten, meinen Handelsroboter auf eine "professionelle Stufe" zu bringen.

Zudem bin ich auch auf absurde Lösungen gestoßen, wie z.B.: DML/DDL-Abläufe (Daten einfügen/aktualisieren/löschen, Objekte in einer Datenbank erzeugen/verwerfen) wurden mittels der Standard libmysql.dll-Datei ausgeführt, und die Datenauswahl (AUSWÄHLEN) war in der Tat als eine HTTP-Suchanfrage (mittels inet.dll) an ein PHP-Script implementiert, das sich auf dem Web-Server auf der MySQL-Serverseite befand. Die SQL-Suchanfragen waren in das PHP-Script geschrieben.

Mit anderen Worten: damit das Projekt laufen konnte, musste man die folgenden Komponenten verfügbar, konfiguriert und laufen haben: MySQL-Server, Apache/IIS Web-Server, PHP/ASP-Scripts auf Serverseite. Eine Kombination einer reichlich großen Zahl an Technologien. Das kann natürlich unter gewissen Umständen akzeptabel sein, doch wenn die einzige Aufgabe darin besteht, Daten aus der Datenbank auszuwählen - dann ist das Quatsch. Darüber hinaus ist die Unterstützung einer derart umständlichen Lösung auch noch zeitaufwendig.

Bei den meisten Lösungen gab es keine Probleme damit, Daten einzufügen, Objekte zu erzeugen und ähnlichem. Das Problem lag in der Datenauswahl, da die Daten an die sie aufrufende Umgebung geliefert werden sollten.

Ich glaubte, für diesen Zweck Arrays zu verwenden, wäre unpraktisch und nicht komfortabel, ganz einfach deshalb, weil ausgewählte Suchanfragen an die Datenbank im Laufe der Entwicklung/Fehlersuche/Support des Hauptprojekts geändert werden können und man zugleich ja auch die korrekte Memory-Zuweisung für die Arrays kontrollieren sollte. Nun, dies kann und muss vermeiden werden.

Die im Folgenden besprochene MQL <-> MySql-Schnittstelle beruht auf einem typischen Ansatz, der in Oracle PL/SQL, MS SQL T-SQL, AdoDB angewendet wird - der Arbeit mit Cursors. Die Schnittstelle wurde mit dem klaren Ziel vor Augen entwickelt, das Programmieren und die Pflege zu erleichtern und noch dazu mit möglichst wenig Komponenten arbeiten zu müssen. Sie ist implementiert als ein DLL-Wrapper für die Standard-Library libmysql.dll und als ein Set an Schnittstellenfunktionen als .mqh-Datei.


1. Die MQL <-> MySQL Schnittstelle

Die Interaktion zwischen dem MetaTrader Terminal (via MQL-Programme) kann mit Hilfe der unten stehenden zwei Komponenten implementiert werden:

Schema der Interaktion von MQL und MySQL

1. Die Schnittstellen-Library MQLMySQL.mqh. Sie wird dem Projekt mit Hilfe des #include Directory hinzugefügt und kann ganz nach Ihrem Geschmack verändert werden.

Sie enthält die Direktiven zum Import von Funktionen der MQLMySQL.dll dynamischen Library sowie auch Funktionen für ihren Aufruf und dem Umgang mit Fehlern.

2. Die MQLMySQL.dll dynamische Library. Sie ist ein Wrapper für den Zugriff auf die Funktionalität der Standard-Library libmysql.dll.

Die MQLMySQL.dll Library verarbeitet zudem auch die Ergebnisse der Abläufe und den gemeinsamen Zugriff auf die Datenbankverbindungen und Cursors. D.h.: Sie können gleichzeitig mehrere Verbindungen erstellen und verwenden (von einem oder mehreren MQL-Programmen), einige Cursors offen lassen, mit Suchanfragen einer oder mehrerer Datenbanken. Der Zugriff auf gemeinsame Ressourcen wird durch Mutexe getrennt.

3. Die Standard dynamische Library libmysql.dll ist ein nativer Zugriffstreiber. Sie können sie aus jeder angebotenen MySQL-Datenbank in C:\Windows\Sytem32 oder <Terminal>\MQL5\Libraries (für MetaTrader 4 in <Terminal>\MQL4\Libraries) kopieren.

Sie ist in der Tat für das Senden von Suchanfragen an die Datenbank und das Abrufen von Ergebnissen verantwortlich.

Bleiben wir noch ein bisschen bei den Hauptpunkten, wie z.B.: Öffnen/Trennen der Verbindung, DML/DDL-Suchanfragen und Datenauswahl ausführen.

1.1 Öffnen und Trennen der Verbindung

Die MySqlConnect Funktion wurde implementiert, damit man die Verbindung zur MySQL-Datenbank öffnen kann:

Typ

Bezeichnung

Parameter

Beschreibung

int

MySqlConnect

Diese Funktion implementiert eine Verbindung mit der Datenbank und liefert eine Anschlusskennung. Diese ID ist für Suchanfragen der Datenbank notwendig.

Im Falle eines Verbindungsabbruchs wird "-1" als Wert geliefert. Alle Einzelheiten zum Fehler sollten sich in den MySQLErrorNumber und MySqlErrorDescription Variablen finden.

Diese Funktion wird typischerweise aufgerufen, wenn dasOnInit() Ereignis im MQL-Programm abgewickelt wird.

string pHost

Der DNS-Name oder die IP-Adresse des MySQL-Servers

string pUser

Datenbanknutzer (z.B. root)

string pPassword

Passwort des Datenbanknutzers

string pDatenbank

Name der Datenbank

int pPort

Der TCP/IP-Port der Datenbank (meist 3306)

string pSocket

Die Unix-Socket (bei Systemen, die auf Unix laufen)

int pClientFlag

Kombination spezieller Marker (meist 0)

Die MySqlDisconnect Schnittstellenfunktion wurde implementiert, um die Verbindung zu trennen:

Typ

Bezeichnung Parameter Beschreibung

void

MySqlDisconnect

Diese Funktion trennt die Verbindung mit der MySQL-Datenbank.

Diese Funktion wird typischerweise aufgerufen, wenn dasOnDeinit() Ereignis im MQL-Programm abgewickelt wird.

int pConnection

Anschlusskennung

Hier sei darauf hingewiesen, dass die MySQL-Datenbank im Falle eines Hardwareausfalls, Netzwerküberlastung oder Timeouts die Verbindung eigenständig trennen kann (wenn über eine lange Zeit keine Suchanfragen an die die Datenbank gesendet wurden).

Entwickler verwenden oft das OnTick() Ereignis, um Daten in die Datenbank zu schreiben. Doch nähert sich dann das Wochenende und der Markt ist geschlossen, ist die Verbindung immer noch "offen". In diesem Fall wird sie von MySQL per Timeout geschlossen (standardmäßige Einstellung: 8 Stunden).

Und montags, wenn der Markt wieder offen ist, werden im Projekt dann Fehler gefunden. Daher wird dringend geraten, die Verbindung zu prüfen und/oder sich nach einem Zeitintervall, der kürzer ist als das in den MySQL-Servereinstellungen eingerichtete Timeout, erneut zu verbinden.

1.2 Ausführung der DML/DDL-Suchanfragen

DML-Abläufe werden zu Datenmanipulationen verwendet (Daten-Manipulations-Sprache). Datenmanipulation umfasst die folgende Reihe an Aussagen: EINFÜGEN, AKTUALISIEREN und LÖSCHEN.

DDL-Abläufe werden zur Datendefinition verwendet (Daten-Definitions-Sprache). Dies beinhaltete die Erzeugung (ERZEUGEN) von Datenbank-Objekten (Tabellen, Ansichten, gespeicherte Vorgänge, Auslöser, usw.) und ihre Veränderung (ÄNDERN) und LÖSCHUNG (STREICHEN).

Doch es sind nicht alles DML/DDL-Aussagen: so wird darüber hinaus DCL (Daten-Kontroll-Sprache) zur Abtrennung des Datenzugriffs verwendet - doch dieses Merkmal von SQL lassen wir hier beiseite. Jeder dieser Befehle kann mit Hilfe der MySqlExecute Schnittstellenfunktion ausgeführt werden:


Typ

Bezeichnung

Parameter

Beschreibung

bool

MySqlExecute

Diese Funktion kann zur Ausführung nicht-AUSGEWÄHLTER Aussagen von SQL verwendet werden, nachdem die Verbindung zur Datenbank erfolgreich eingerichtet worden ist (mittels der MySqlConnect Funktion).

Wurde ein Befehl erfolgreich ausgeführt, liefert die Funktion 'true', andernfalls 'false'. Alle Einzelheiten zu Fehlern sollten sich in den MySQLErrorNumber und MySqlErrorDescription Variablen finden.

int pConnection

Anschlusskennung

string pQuery

SQL-Suchanfrage

Als eine SQL-Suchanfrage können Sie auch den Befehl VERWENDEN für die Auswahl der Datenbank verwenden. Ich würde hier gerne kurz die Verwendung von Suchanfragen mit mehreren Aussagen erwähnen. Dies ist eine Reihe von SQL-Befehlen, die durch einen Strichpunkt (";" )getrennt sind.

Um den Modus mit mehreren Aussagen zu aktivieren, sollte die Verbindung zur Datenbank mit dem CLIENT_MULTI_STATEMENTS Marker geöffnet werden:

...
int ClientFlag = CLIENT_MULTI_STATEMENTS; // Setting the multi-statements flag
int DB; 

DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag); // Connection to the database

if (DB == -1)
   {
    // Handling the connection error
   }
...

// Preparing a SQL query to insert data (3 rows in one query)
string SQL;
SQL = "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3601,1.3632);";
SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3621,1.3643);";
SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3605,1.3629);";
...

if (!MySqlExecute(DB,SQL)) 
   {
    // Showing an error message
   }
...

In diesem Fragment werden 3 Einträge in die EURUSD-Tabelle mit einem einzigen Aufruf zur Datenbank eingefügt. Jede der in der SQL-Variable gespeicherten Suchanfragen ist durch einen Strichpunkt (";") abgetrennt.

Dieser Ansatz kann für häufiges Einfügen/Aktualisieren/Löschen verwendet werden. Eine Reihe erforderlicher Befehle wird in ein "Paket" vereint - für weniger Netzwerk-Traffic und einer verbesserten Datenbankleistung.

Die EINFÜGEN-Syntax in MySQL ist in Bezug auf den Umgang mit Ausnahmen ziemlich gut entwickelt.

Besteht die Aufgabe z.B. darin, die Kurshistorie zu verschieben, sollte für die Währungspaare eine Tabelle erzeugt werden, mit dem primären Schlüssel vom Typ 'datetime', da das Datum und die Zeit eines Bars ja einzigartig sind. Des weiteren sollte geprüft werden, ob die Daten auf jedem bestimmten Bar in der Datenbank vorhanden sind (zur Verbesserung der Stabilität der Datenmigration). Diese Prüfung ist bei MySQL nicht erforderlich, da die EINFÜGEN Aussage BEI ZWEITSCHLÜSSEL unterstützt.

Einfacher ausgedrückt, kann bei einem Versuch ,Daten einzufügen und wenn die Tabelle bereits einen Eintrag mit dem selben Datum und Uhrzeit hat, für diese Reihe die Aussage EINFÜGEN ignoriert oder durch AKTUALISIEREN ersetzt werden (vgl. http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html).

1.3 Datenauswahl

Die SQL AUSWÄHLEN Aussage wird zum Abrufen von Daten von der Datenbank verwendet. Die unten stehende Handlungsabfolge wird zur Auswahl von Daten und dem Abrufen des Ergebnis der Auswahl verwendet:

  1. Die AUSWÄHLEN Aussage vorbereiten.
  2. Cursor öffnen.
  3. Die Anzahl der Reihen, die von der Suchanfrage geliefert werden, erhalten.
  4. Jede Reihe der Suchanfrage in der Schleife wiederholen und abrufen.
  5. Daten auf den MQL-Variablen innerhalb der Schleife holen.
  6. Cursor wieder schließen.

Dies ist natürlich ein generelles Schema, daher sind nicht jedes Mal alle Abläufe komplett vonnöten. Wollen Sie z.B. sicherstellen, dass eine Reihe in der Tabelle (nach jedem Kriterium) besteht, dann genügt es, eine Suchanfrage vorzubereiten, den Cursor zu öffnen, die Anzahl der Reihen zu bekommen und den Cursor wieder zu schließen. Die obligatorischen Schritte sind eigentlich: die AUSWÄHLEN Aussage vorbereiten und den Cursor öffnen und schließen.

Was ist ein Cursor? Ein Cursor ist ein Verweis auf den Kontext-Speicherbereich, also das daraus folgende Set an Werten. Wenn Sie die AUSWÄHLEN Suchanfrage absenden, weist die Datenbank dem Ergebnis Memory zu und erzeugt einen Zeiger auf eine Reihe, der von einer zur anderen Reihe gehen kann. Somit kann auf alle Reihen in der Reihenfolge einer von der Suchanfrage festgelegten Warteschlange zugegriffen werden (ORDNEN NACH Satz der AUSWÄHLEN Aussage).

Für die Datenauswahl werden die folgenden Schnittstellenfunktionen verwendet:

Den Cursor öffnen:

Typ

Bezeichnung

Parameter

Beschreibung

int

MySqlCursorOpen

Diese Funktion öffnet einen Cursor für die Suchanfrage AUSWÄHLEN und liefert bei Erfolg einen Cursor-Identifikator. Ansonsten liefert die Funktion "-1". Alle Einzelheiten zu Fehlern sollten sich in den MySQLErrorNumber und MySqlErrorDescription Variablen finden.

int pConnection

Identifikator der Verbindung mit der Datenbank

string pQuery

SQL-Suchanfrage (die AUSWÄHLEN Aussage)

Die Anzahl Reihen, die von der Suchanfrage geliefert werden, erhalten:

Typ

Bezeichnung

Parameter

Beschreibung

int

MySqlCursorRows

Diese Funktion liefert die Anzahl an Reihen, die von der Suchanfrage ausgewählt wurde.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

Die Reihe mit den Suchanfragen holen:

Typ

Bezeichnung

Parameter

Beschreibung

bool

MySqlCursorFetchRow

Holt eine Reihe aus dem von der Suchanfrage gelieferten Datenset. Nach erfolgreicher Ausführung können Sie die Daten zu MQL-Variablen abfragen. Bei Erfolg liefert die Funktion 'true'; andernfalls 'false'.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

Holt Daten in MQL-Variablen nach dem Abrufen der Suchanfrage-Reihe:

Typ

Bezeichnung

Parameter

Beschreibung

int

MySqlGetFieldAsInt

Diese Funktion liefert die Abbildung des Tabellenfeld-Werts mit Hilfe des int Datentyps.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

int pField

Die Feldnummer in der AUSWÄHLEN-Liste (Nummerierung beginnt bei 0)

double

MySqlGetFieldAsDouble

Diese Funktion liefert die Abbildung des Tabellenfeld-Werts mit Hilfe des double Datentyps.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

int pField

Die Feldnummer in der AUSWÄHLEN-Liste (Nummerierung beginnt bei 0)

datetime

MySqlGetFieldAsDatetime

Diese Funktion liefert die Abbildung des Tabellenfeld-Werts mit Hilfe des datetime Datentyps.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

int pField

Die Feldnummer in der AUSWÄHLEN-Liste (Nummerierung beginnt bei 0)

string

MySqlGetFieldAsString

Diese Funktion liefert die Abbildung des Tabellenfeld-Werts mit Hilfe des string Datentyps.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

int pField

Die Feldnummer in der AUSWÄHLEN-Liste (Nummerierung beginnt bei 0)


Alle von MySQL gelieferten Daten besitzen eine Darstellung (dargestellt ohne Typen als Strings).

Sie können daher, wenn Sie diese Funktionen verwenden, die ausgewählten Daten im gewünschten Typ auswerfen. Der einzige Nachteil ist die Spezifizierung der Spaltennummer (Nummerierung beginnt bei 0) in der AUSWÄHLEN-Liste, anstelle ihres Namens. Bei der Entwicklung einer Anwendung befinden sich jedoch die Vorbereitung der AUSWÄHLEN Aussage und das Abrufen der Ergebnisse fast immer auf einer Seite, daher können Sie, wenn Sie die Logik für den Datenabruf vorschreiben, die AUSWÄHLEN-Aussage sehen.

Somit wissen Sie immer die Anzahl der Datenfelder in der AUSWÄHLEN-Liste (dieser Ansatz wird auch beim Datenzugriff mit Hilfe von AdoDB verwendet). Dieser Teil kann in Zukunft durchaus noch weiter verfeinert werden. Doch dies hat nur wenig Einfluss auf die Funktionalität der entwickelten Lösung.

Cursor wieder schließen:

Typ

Bezeichnung

Parameter

Beschreibung

void

MySqlCursorClose

Diese Funktion schließt den angegebenen Cursor und gibt den Speicher frei.

int pCursorID

Der von MySqlCursorOpen gelieferte Cursor-Identifikator

Das Schließen des Cursors ist ein entscheidender Vorgang, daher: nicht vergessen, einen Cursor zu schließen.

Stellen Sie sich vor, Sie öffnen den Cursor und vergessen anschließend ihn wieder zu schließen. Angenommen, Daten werden an den Cursor bei jeder Kursschwankung während des Umgangs mit dem OnTick() Ereignis abgerufen, und jedes Mal, wenn ein neuer Cursor geöffnet wird, wird ihm Memory zugewiesen (sowohl auf Client- als auch auf Serverseite). Irgendwann wird der Service dies dann ablehnen, da die Obergrenze offener Cursors erreicht ist und das zu einem Pufferüberfluss führen kann.

Klar habe ich hier etwas übertrieben, doch das kann durchaus ein Ergebnis bei der Arbeit mit libmysql.dll direkt sein. Die MQLMySQL.DLL dynamische Llibrary verteilt jedoch für Cursors Memory und wird keine Cursor mehr öffnen, der die zulässige Grenze überschritten hat.

Bei der Implementierung echter Aufgaben, genügt es völlig, wenn 2-3 Cursors offen sind. Jeder Cursor kann eine kartesische Datenmessung bewältigen, sodass der Einsatz von zwei-drei Cursors gleichzeitig (z.B. verschachtelt, wenn ein Cursor von seinen Parametern her von einem anderen Cursor abhängt) zwei bis drei Dimensionen abdeckt. Für die meisten Aufgaben ist das vollkommen ausreichend. Zusätzlich können Sie für die Implementierung komplexer Datenauswahl auch immer diese Objekte zur Abbildung der Datenbank benutzen (ANSEHEN), sie serverseitig erzeugen und was Tabellen betrifft, ihnen vom MQL-Code aus Suchanfragen senden.

1.4 Zusätzliche Information

Im Folgenden einige zusätzliche Merkmale:

1.4.1. Daten von einer .INI-Datei lesen

Typ

Bezeichnung

Parameter

Beschreibung

String

ReadIni

Liefert den Wert eines Schlüssels des gegebenen Bereichs der INI-Datei.

string pFileName

Name der INI-Datei

string pSection

Name des Bereichs

string pKey

Name des Schlüssels


Oft ist das Speichern von Informationen über Verbindungen zur Datenbank (IP-Adresse des Servers, Port, Benutzername, Passwort, usw.) direkt im MQL-Code (oder den Parametern des Expert Advisors, Indikators des Scripts) nicht logisch, da der Server verschoben werden, sich seine Adresse dynamisch ändern kann, usw. In so einem Fall müssen Sie den MQL-Code ändern. Daher empfiehlt es sich, all diese Daten in der Standard .INI-Datei abzulegen, und nur ihren Namen in das MQL-Programm zu schreiben. Um die Verbindungsparameter lesen und nutzen zu können, verwenden Sie anschließend die ReadINI-Funktion.

Die INI-Datei enthält z.B. folgende Informationen:

[MYSQL]
Server = 127.0.0.1
User = root
Password = Adm1n1str@t0r
Database = mysql
Port = 3306

Um die IP-Adresse des Servers zu bekommen, folgendes ausführen:

string vServer = ReadIni("C:\\MetaTrader5\\MQL5\\Experts\\MyConnection.ini", "MYSQL", "Server");

Die INI-Datei befindet sich in C:\MetaTrader5\MQL5\Experts und heißt "MyConnection.ini"; Sie greifen auf den Server-Schlüssel des MYSQL-Bereichs zu. Sie können in einer INI-Datei Einstellungen für verschiedene Server, die in Ihrem Projekt verwendet werden, speichern.

1.4.2. Problembereiche nachverfolgen

In der Schnittstellen-Library gibt es den Nachverfolgungs-Modus, der zur Fehlersuche von SQL-Suchanfragen an jeder Stelle im MQL-Programm aktiviert werden kann.

Geben Sie im Problembereich folgendes an:

SQLTrace = true;

und dann

SQLTrace = false;

Wenn Sie 'Nachverfolgen' zu Beginn des MQL-Programms aktivieren und es dann nicht wieder deaktivieren, werden alle Aufrufe an die Datenbank protokolliert. Das Protokoll wird in der Terminal-Konsole verwahrt (mit Hilfe des Befehls 'Print' ).


2. Beispiele

Dieser Bereich bietet einige Beispiele der Verbindung und der Verwendung der entwickelten Libraries. Betrachten Sie sie und schätzen die Verwendbarkeit der Software-Lösung ab.

Das Beispiel MySQL-003.mq5 zeigt z.B. folgendes: mit einer Datenbank verbinden (Verbindungsparameter werden in der .ini-Datei gespeichert), erzeugen einer Tabelle, einfügen von Daten (auch mit Hilfe von Mehrfach-Aussagen) und trennen der Verbindung zur Datenbank.

//+------------------------------------------------------------------+
//|                                                    MySQL-003.mq5 |
//|                                   Copyright 2014, Eugene Lugovoy |
//|                                              https://www.mql5.com |
//| Inserting data with multi-statement (DEMO)                       |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Eugene Lugovoy."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <MQLMySQL.mqh>

string INI;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
 string Host, User, Password, Database, Socket; // database credentials
 int Port,ClientFlag;
 int DB; // database identifier
 
 Print (MySqlVersion());

 INI = TerminalInfoString(TERMINAL_PATH)+"\\MQL5\\Scripts\\MyConnection.ini";
 
 // reading database credentials from INI file
 Host = ReadIni(INI, "MYSQL", "Host");
 User = ReadIni(INI, "MYSQL", "User");
 Password = ReadIni(INI, "MYSQL", "Password");
 Database = ReadIni(INI, "MYSQL", "Database");
 Port     = (int)StringToInteger(ReadIni(INI, "MYSQL", "Port"));
 Socket   = ReadIni(INI, "MYSQL", "Socket");
 ClientFlag = CLIENT_MULTI_STATEMENTS; //(int)StringToInteger(ReadIni(INI, "MYSQL", "ClientFlag"));  

 Print ("Host: ",Host, ", User: ", User, ", Database: ",Database);
 
 // open database connection
 Print ("Connecting...");
 
 DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag);
 
 if (DB == -1) { Print ("Connection failed! Error: "+MySqlErrorDescription); } else { Print ("Connected! DBID#",DB);}
 
 string Query;
 Query = "DROP TABLE IF EXISTS `test_table`";
 MySqlExecute(DB, Query);
 
 Query = "CREATE TABLE `test_table` (id int, code varchar(50), start_date datetime)";
 if (MySqlExecute(DB, Query))
    {
     Print ("Table `test_table` created.");
     
     // Inserting data 1 row
     Query = "INSERT INTO `test_table` (id, code, start_date) VALUES ("+(string)AccountInfoInteger(ACCOUNT_LOGIN)+",\'ACCOUNT\',\'"+TimeToString(TimeLocal(), TIME_DATE|TIME_SECONDS)+"\')";
     if (MySqlExecute(DB, Query))
        {
         Print ("Succeeded: ", Query);
        }
     else
        {
         Print ("Error: ", MySqlErrorDescription);
         Print ("Query: ", Query);
        }
     
     // multi-insert
     Query =         "INSERT INTO `test_table` (id, code, start_date) VALUES (1,\'EURUSD\',\'2014.01.01 00:00:01\');";
     Query = Query + "INSERT INTO `test_table` (id, code, start_date) VALUES (2,\'EURJPY\',\'2014.01.02 00:02:00\');";
     Query = Query + "INSERT INTO `test_table` (id, code, start_date) VALUES (3,\'USDJPY\',\'2014.01.03 03:00:00\');";
     if (MySqlExecute(DB, Query))
        {
         Print ("Succeeded! 3 rows has been inserted by one query.");
        }
     else
        {
         Print ("Error of multiple statements: ", MySqlErrorDescription);
        }
    }
 else
    {
     Print ("Table `test_table` cannot be created. Error: ", MySqlErrorDescription);
    }
 
 MySqlDisconnect(DB);
 Print ("Disconnected. Script done!");
}

Das Beispiel MySQL-004.mq5 zeigt die Auswahl von Daten aus einer Tabelle, die vom "MySQL-003.mq5" Script angelegt wurde. 

//+------------------------------------------------------------------+
//|                                                    MySQL-004.mq5 |
//|                                   Copyright 2014, Eugene Lugovoy |
//|                                              https://www.mql5.com |
//| Select data from table (DEMO)                                    |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Eugene Lugovoy."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <MQLMySQL.mqh>

string INI;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
 string Host, User, Password, Database, Socket; // database credentials
 int Port,ClientFlag;
 int DB; // database identifier
 
 Print (MySqlVersion());

 INI = TerminalInfoString(TERMINAL_PATH)+"\\MQL5\\Scripts\\MyConnection.ini";
 
 // reading database credentials from INI file
 Host = ReadIni(INI, "MYSQL", "Host");
 User = ReadIni(INI, "MYSQL", "User");
 Password = ReadIni(INI, "MYSQL", "Password");
 Database = ReadIni(INI, "MYSQL", "Database");
 Port     = (int)StringToInteger(ReadIni(INI, "MYSQL", "Port"));
 Socket   = ReadIni(INI, "MYSQL", "Socket");
 ClientFlag = (int)StringToInteger(ReadIni(INI, "MYSQL", "ClientFlag"));  

 Print ("Host: ",Host, ", User: ", User, ", Database: ",Database);
 
 // open database connection
 Print ("Connecting...");
 
 DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag);
 
 if (DB == -1) { Print ("Connection failed! Error: "+MySqlErrorDescription); return; } else { Print ("Connected! DBID#",DB);}
 
 // executing SELECT statement
 string Query;
 int    i,Cursor,Rows;
 
 int      vId;
 string   vCode;
 datetime vStartTime;
 
 Query = "SELECT id, code, start_date FROM `test_table`";
 Print ("SQL> ", Query);
 Cursor = MySqlCursorOpen(DB, Query);
 
 if (Cursor >= 0)
    {
     Rows = MySqlCursorRows(Cursor);
     Print (Rows, " row(s) selected.");
     for (i=0; i<Rows; i++)
         if (MySqlCursorFetchRow(Cursor))
            {
             vId = MySqlGetFieldAsInt(Cursor, 0); // id
             vCode = MySqlGetFieldAsString(Cursor, 1); // code
             vStartTime = MySqlGetFieldAsDatetime(Cursor, 2); // start_time
             Print ("ROW[",i,"]: id = ", vId, ", code = ", vCode, ", start_time = ", TimeToString(vStartTime, TIME_DATE|TIME_SECONDS));
            }
     MySqlCursorClose(Cursor); // NEVER FORGET TO CLOSE CURSOR !!!
    }
 else
    {
     Print ("Cursor opening failed. Error: ", MySqlErrorDescription);
    }
    
 MySqlDisconnect(DB);
 Print ("Disconnected. Script done!");
}

Die o.g. Beispiele enthalten den typischen Umgang mit Fehlern, wie er in echten Projekten verwendet wird.

Eigentlich sollte jede, in einem MQL-Programm verwendete Suchanfrage in jedem MySQL-Client (PHPMyAdmin, DB Ninja, MySQL-Konsole) auf Fehler untersucht werden. Ich persönlich verwende und empfehle professionelle Software für Datenbank-Entwicklung - Quest TOAD für MySQL.


Fazit

Dieser Beitrag beschreibt nicht die Einzelheiten der Implementierung von MQLMySQL.DLL, das in der Microsoft Visual Studio 2010 (C/C++) Umgebung entwickelt wurde. Diese Software ist für praktische Anwendung ausgelegt und besitzt mehr als 100 erfolgreiche Implementierungen in verschiedenen Bereichen der MQL-Softwareentwicklung (von der Erzeugung komplexer Handelssysteme bis hin zu Web-Publishing).

  • Die Library-Versionen für MQL4 und MQL5 sind unten angehängt. In diesen Anhängen findet sich auch eine .zip-Datei mit dem Quellcode von MQLMySQL.DLL;
  • Entsprechende Dokumentationen finden Sie in den Archiven;
  • Um diese Beispiele anzuwenden, vergessen Sie nicht, die Parameter der Verbindung mit Ihrer Datenbank in der Datei \Scripts\MyConnection.ini festzulegen.

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

Beigefügte Dateien |
MQLMySQL_for_MQL4.zip (1124.85 KB)
MQLMySQL_for_MQL5.zip (1125.19 KB)
Der MQL5-Assistent: Platzierung von Order, Stop-Losses und Take Profits auf berechneten Kursen. Erweiterung der Standard-Library Der MQL5-Assistent: Platzierung von Order, Stop-Losses und Take Profits auf berechneten Kursen. Erweiterung der Standard-Library
Dieser Artikel beschreibt die MQL5 Standard Library-Erweiterung, mit der Expert Advisors erzeugt und Order sowie Stop Losses und Take Profits mittels des MQL5-Assistenten nach Kursen, die von den mit aufgenommenen Modulen empfangen werden, platziert werden können. Dieser Ansatz bedeutet keinerlei zusätzliche Beschränkungen hinsichtlich der Menge an Modulen und führt auch zu keinerlei Konflikten bei ihrer gemeinsamen Arbeit.
Aufbau eines Social-Technology Startups, Teil I: Ihre MetaTrader 5 Signale twittern Aufbau eines Social-Technology Startups, Teil I: Ihre MetaTrader 5 Signale twittern
Heute erfahren wir, wie man einen MetaTrader 5 Terminal mit Twitter verbindet, damit Sie die Handelssignale Ihres EAs twittern können . Wir entwickeln ein Soziales Entscheidungsunterstützungssystem in PHP, das auf einem RESTful Webdienst beruht. Diese Idee stammt von einem besonderen Konzept automatischen Handels, dem sog. computergestützten Handel. Wir möchten, dass die kognitiven Fähigkeiten von tatsächlichen Händlern, diese Handelssignale filtern, die sonst von Expert Advisors automatisch auf dem Markt platziert werden würden.
Universeller Expert Advisor: Pending Orders und Hedging Support (Part 5) Universeller Expert Advisor: Pending Orders und Hedging Support (Part 5)
Dieser Artikel enthält eine weitere Beschreibung der CStrategy Trading Engine. Auf vielfachen Wunsch der Nutzer, haben wir dieser Trading Engine noch die Unterstützung von wartenden/schwebenden (pending) Orders hinzugefügt. Zudem unterstützt die neueste Version von Metatrader 5 nun auch Konten mit einer hedging Option. Diese Unterstützung wurde auch der CStrategy hinzugefügt. Dieser Artikel bietet eine detaillierte Beschreibung der Algorithmen und für die Verwendung von Pending Orders, sowie die Prinzipien der Verwendung der CStartegy mit Konten, welchen die hedging-Option aktiviert haben.
Geschichten von Handelsrobotern: Ist weniger mehr? Geschichten von Handelsrobotern: Ist weniger mehr?
In "The Last Crusade" vor zwei Jahren haben wir eine ziemlich interessante, doch derzeit nicht oft eingesetzte Methode zur Anzeige von Marktinformationen untersucht - die Point-&Figure-Charts. Jetzt schlage ich Ihnen vor, einen Handelsroboter auf Basis der auf dem Point-&Figure-Chart entdeckten Mustern zu schreiben.