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 150 292
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)
Letzte Kommentare | Zur Diskussion im Händlerforum (292)
Alex Renko
Alex Renko | 1 Nov. 2024 in 18:28
Hallo! Frage für Experten - wie viele Daten und wie oft kann ich von MySQL zu MT5 lesen?
Zum Beispiel habe ich Daten 50 000 Elemente und ich aktualisieren sie in der Tabelle alle 0,1 sec (Zahlen). Ist MT5 in der Lage, diese Daten aus MySQL zu übernehmen und sie alle 0,1 Sekunden zu aktualisieren? Gibt es irgendeine Einschränkung der in diesem Artikel beschriebenen Funktionalität in Bezug auf KB pro 1 Abfrage?
Eugeniy Lugovoy
Eugeniy Lugovoy | 13 Dez. 2024 in 15:47
Alex Renko #:
Hallo! Frage für Experten - wie viele Daten und wie oft kann ich von MySQL zu MT5 lesen?
Zum Beispiel habe ich Daten 50 000 Elemente und ich aktualisieren sie in der Tabelle alle 0,1 sec (Zahlen). Ist MT5 in der Lage, diese Daten aus MySQL zu übernehmen und sie alle 0,1 Sekunden zu aktualisieren? Gibt es irgendeine Einschränkung der Funktionalität in diesem Artikel auf KB für 1 Abfrage gegeben?

Nun, die Frage ist sicherlich interessant ...

Ich muss sagen, dass es keine Beschränkungen für die Anzahl der zurückgegebenen SELECT-Abfragezeilen gibt.

Die Größenbeschränkung für die Abfrage selbst liegt bei 64 Kb. Wenn Sie also versuchen, 50k Datenzeilen zu aktualisieren, ist es besser, sie in Stapel aufzuteilen, z.B. je 1000 Zeilen, und somit 50 Abfragen zu senden.

Was die Geschwindigkeit von 100 ms betrifft, so werden Sie, wenn Sie die Datenbank auf einem Server haben und Ihr Terminal, auf dem Sie MQL mit einer Verbindung zur Datenbank ausführen, etwas entfernt ist, höchstwahrscheinlich auf eine Netzwerklatenz in der Größe des ping.... stoßen.

Wenn Sie zum Beispiel einen Ping von 60 ms zwischen dem Datenbankserver und dem Terminal haben, wird die tatsächliche Antwort vom Server verzögert = 60 ms (Abfrage) + Abfrageverarbeitungszeit auf der Datenbankseite + 60 ms (Antwort).


Dieses Projekt ist nur ein einfacher Wrapper für den Zugriff auf die Funktionalität der dynamischen mysql-Bibliotheken.

Der Funktionsumfang ist auf die wichtigsten praktisch nützlichen Funktionen beschränkt, man kann es erweitern, hinzufügen, was man braucht, z.B. Unterstützung für asynchrone Abfragen hinzufügen, und dann kann man alle 50 Abfragen auf 1000 Zeilen senden, ohne auf die Ausführung jeder einzelnen zu warten.


P.S.: Auf Github können Sie die Quellen der Bibliothek und die voreingestellten Grenzen sehen(https://github.com/elugovoy/MQLMySQL-Project/tree/master/MQLMySQL).

P.P.S.: Sie können die Bibliothek auch herunterladen, nach eigenem Ermessen verändern, kompilieren und testen.

Eugeniy Lugovoy
Eugeniy Lugovoy | 13 Dez. 2024 in 15:58
andreysneg #:

Gibt es eine Möglichkeit, die Anzahl der Felder zu kennen, die nach MySqlCursorFetchRow abgerufen werden können?

Vielleicht gibt es eine versteckte Funktion wie RowFieldsSize ...


Wie ich verstehe, wenn es kein Feld gibt, gibt MySqlGetFieldAsString leer zurück. Aber auch, wenn String Field speziell ein leeres Feld enthält, gibt es auch leer zurück. D.h. es ist nicht immer möglich, die Anzahl der Felder mit brachialer Gewalt zu ermitteln.


Als Krücke kann man zwar erst per sql-Befehl herausfinden, dann aber wieder selektieren, aber das ist schon ein unnötiges Select.



Bitte entwickeln Sie die Bibliothek, sehr nützliche Sache. Natürlich, ein paar mysql sollte in mt vor langer Zeit gebaut werden

Hm... und welche Art von Abfragen sind so knifflig bei Ihnen, dass es notwendig ist, die Anzahl der Felder, die von ihm zurückgegeben werden, zu bestimmen?

Normalerweise wird im SELECT-Befehl nur aufgeführt, was in einer bestimmten Situation benötigt wird. Verwenden Sie nicht SELECT *, sondern wählen Sie nur das aus, was Sie brauchen :) das ist normale Praxis.

Sie sollten keine Krücken machen, Sie können den Quellcode von Github nehmen und einen Wrapper für mysql_fetch_fields() MySQL API Funktion hinzufügen

Eugeniy Lugovoy
Eugeniy Lugovoy | 13 Dez. 2024 in 16:03
andreysneg #:

Abfrage einfügen und aktualisieren - nur 16kb Abfragelimit ?


Wenn die Abfrage mehr als 16.000 Zeichen ist, stürzt metatrader ab (schließt). wenn weniger, ist es in Ordnung.

Ich füge ein Beispiel von UPDATE für 32.000 Zeichen bei.


Feld zum Aktualisieren in der Datenbank - LONGTEXT

Die Bibliothek definiert die Größe für Abfragen in 64kb:

#define MAX_QUERY_SIZE 65535 // Maximale Größe der SQL-Abfrage

Ich nehme an, in Ihrem Fall (und wahrscheinlich nicht nur in Ihrem Fall, sondern in MQL String) gibt es 4-Byte-UTF-Kodierung, das heißt 16*4 = 64 und die Grenze ist erreicht....

Hier entweder Abfragen aufteilen oder den Puffer für die Abfrage vergrößern und neu kompilieren.

Gabms
Gabms | 15 Jan. 2025 in 21:01

Wahnsinn!

Seine "zu laut" verwenden SELECTs mit diesem Wrapper in OnTick() Funktion?

Dank.

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.