English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Come accedere al database MySQL da MQL5 (MQL4)

Come accedere al database MySQL da MQL5 (MQL4)

MetaTrader 5Integrazione | 12 gennaio 2022, 11:20
221 0
Eugeniy Lugovoy
Eugeniy Lugovoy

Introduzione

Il problema dell'interazione di MQL con i database non è nuovo, tuttavia è ancora molto rilevante. L'uso di database può aumentare notevolmente le possibilità di utilizzo di MetaTrader, come l’archiviazione e l’analisi della cronologia dei prezzi, la copia delle operazioni da una piattaforma di trading a un'altra, la fornitura di quotazioni/negoziazioni in tempo reale, calcoli analitici complessi eseguiti dal server e/o l’utilizzo di un programma di scheduling, il monitoraggio e controllo remoto degli account tramite tecnologie web.

Ci sono stati molti tentativi di utilizzare al meglio la combinazione di MQL e MySQL, alcune di queste soluzioni sono disponibili nel CodeBase.

Ad esempio "MySQL wrapper - libreria per MetaTrader 4" è il progetto dal quale molti programmatori iniziano a sviluppare i propri prodotti, per poi aggiungere elementi. Uno degli svantaggi di questa soluzione, secondo me, è l'allocazione di array speciali per la lettura dei dati dal database.

Questo altro progetto "MySQL logger 1 - EA per MetaTrader 4" è altamente specializzato e non utilizza wrapper per accedere alla libreria standard libmysql.dll. Di conseguenza non funziona in MetaTrader4 Build 600+, poiché i tipi di carattere char sono stati sostituiti da wchar_t e l'uso del tipo int al posto del puntatore della struttura TMYSQL causa perdite di memoria nel progetto (la memoria allocata non può essere controllata/ liberata).

Un altro progetto interessante è "EAX_Mysql - libreria MySQL - libreria per MetaTrader 5". È una buona implementazione. Gli svantaggi elencati dall’autore impongono alcune restrizioni al suo utilizzo.

Chiunque abbia bisogno di utilizzare i database nei propri progetti MQL ha due opzioni: o sviluppa la propria soluzione e ne apprende ogni singola parte, oppure può utilizzare/adattare qualsiasi soluzione di terze parti, imparando ad utilizzarla e rilevandone tutti i difetti che possono essere da ostacolo al progetto.

Ho avuto questo dilemma durante lo sviluppo di un trading robot piuttosto complesso. Dopo aver cercato tra i progetti esistenti e studiato un numero molto elevato di soluzioni, mi sono reso conto che nessuna delle implementazioni trovate poteva portare il mio robot di trading ad un ‘livello professionale’.

Inoltre, ho riscontrato anche soluzioni assurde, ad esempio: Le operazioni DML/DDL (inserisci/aggiorna/elimina dati, crea/rilascia oggetti nel database) sono state eseguite utilizzando lo standard libmysql.dll e la selezione dei dati (SELECT) è stata effettivamente implementata come una richiesta HTTP (usando inet.dll) a un PHP script situato sul server Web sul lato server MySQL. Le query SQL erano scritte nello script PHP.

In altre parole, per eseguire il progetto era necessario mantenere disponibili, configurati e funzionanti i seguenti componenti: Server MySQL, server web Apache/IIS, script PHP/ASP sul lato server... Una combinazione di un numero piuttosto elevato di tecnologie. Naturalmente, in alcune circostanze questo può essere accettabile, ma quando l'unico compito da svolgere è la selezione di dati dal database, questo rappresenta uno spreco di risorse. Inoltre, supportare una soluzione così pesante richiede molto tempo.

Per la maggior parte delle soluzioni non ci sono stati problemi nell’inserimento dei dati, nella creazione di oggetti ecc. Il problema era la selezione dei dati, poiché i dati dovrebbero essere restituiti all'ambiente chiamante.

Pensavo che usare gli array per questo scopo fosse poco pratico e scomodo, semplicemente perché nel corso dello sviluppo/debug/supporto del progetto principale, le query di selezione del database possono essere modificate, mentre si dovrebbe anche controllare la corretta allocazione della memoria per gli array... Questo può e deve essere evitato.

L'interfaccia MQL <-> MySql, di cui parliamo qui sotto, si basa su un approccio tipico, utilizzato in Oracle PL/SQL, MS SQL T-SQL, AdoDB - uso di cursori. Questa interfaccia è stata sviluppata avendo come obbiettivo la facilità di programmazione e manutenzione, oltre all’utilizzo di un minimo numero di componenti. È implementata come wrapper DLL per la libreria standard libmysql.dll e come un insieme di funzioni di interfaccia come il file .mqh.


1. MQL <-> Interfaccia MySQL

L'interazione con il terminale MetaTrader (tramite programmi MQL) può essere implementata con l'aiuto dei seguenti componenti:

schema di interazione MQL e MySQL

1. La libreria di interfaccia MQLMySQL.mqh. Viene aggiunta al progetto utilizzando la directory #include e può essere modificata a proprio piacimento.

Contiene le direttive per l'importazione delle funzioni della libreria dinamica MQLMySQL.dll, nonché le funzioni per poterle chiamare e gestire gli errori.

2. La libreria dinamica MQLMySQL.dll. È un wrapper per accedere alle funzionalità della libreria standard libmysql.dll.

Inoltre, la libreria MQLMySQL.dll elabora i risultati delle operazioni e l'accesso condiviso alle connessioni e ai cursori del database. Questo significa che si possono creare e utilizzare più connessioni contemporaneamente (da uno o più programmi MQL), tenere aperti alcuni cursori, con query a uno o più database. I mutex vengono utilizzati per separare l'accesso alle risorse condivise.

3. La libreria dinamica standard libmysql.dll è un driver di accesso nativo. Puoi copiarlo da qualsiasi distribuzione del database MySql in C:\Windows\Sytem32 o <Terminal>\MQL5\Libraries (per MetaTrader 4 in <Terminal>\MQL4\Libraries).

È infatti responsabile dell'invio delle query al database e del recupero dei risultati.

Soffermiamoci sui punti principali: apertura/chiusura della connessione, esecuzione di query DML/DDL e selezione dei dati.

1.1. Apertura e chiusura della connessione

È stata implementata la funzione MySqlConnect per l'apertura della connessione con il database MySQL:

Tipo

Nome

Parametri

Descrizione

int

MySqlConnect

Questa funzione implementa la connessione con il database e restituisce un identificatore di connessione. Questo ID sarà necessario per interrogare il database.

In caso di errore di connessione, il valore restituito è "-1". Per i dettagli sull'errore, controlla le variabili MySQLErrorNumber e MySqlErrorDescription.

In genere, questa funzione viene chiamata quando si gestisce l'evento OnInit() nel programma MQL.

stringa pHost

Il nome DNS o l'indirizzo IP del server MySQL

stringa pUser

Utente del database (ad esempio, root)

stringa pPassword

La password dell'utente del database

stringa pDatabase

il nome del database

int pPort

La porta TCP/IP del database (solitamente 3306)

stringa pSocket

Il socket Unix (per i sistemi basati su Unix)

int pClientFlag

La combinazione di flag speciali (solitamente 0)

È stata implementata la funzione di interfaccia MySqlDisconnect per la chiusura della connessione:

Tipo

Nome Parametri Descrizione

void

MySqlDisconnect

Questa funzione chiude la connessione con il database MySQL.

In genere, questa funzione viene chiamata quando si gestisce l'evento OnDeinit() nel programma MQL.

int pConnection

Identificatore di connessione

Va notato che il database MySQL può chiudere la connessione da solo in caso di guasto hardware, congestione della rete o timeout (quando non vengono inviate query al database per molto tempo).

Spesso gli sviluppatori utilizzano l'evento OnTick() per scrivere dati nel database. Tuttavia, quando arriva il fine settimana e il mercato è chiuso, il collegamento è ancora "sospeso". In questo caso, MySQL chiuderà per timeout (il valore predefinito è 8 ore).

E il lunedì, quando il mercato riapre, ci saranno errori nel progetto. Pertanto si consiglia vivamente di verificare la connessione e/o riconnettersi al database dopo un intervallo di tempo inferiore al timeout specificato nelle impostazioni del server MySQL.

1.2. Esecuzione di query DML/DDL

Le operazioni DML vengono utilizzate per la manipolazione dei dati (Data Manipulation Language). Le manipolazioni dei dati includono la seguente serie di istruzioni: INSERISCI, AGGIORNA e ELIMINA.

Le operazioni DDL vengono utilizzate per la definizione dei dati (Data Definition Language). Queste includono la creazione (CREATE) di oggetti di database (tabelle, visite, stored procedure, trigger, ecc.) e la loro modifica (ALTER) e cancellazione (DROP).

Queste non sono tutte le istruzioni DML/DDL. Inoltre, viene utilizzato DCL (Data Control Language) per separare l'accesso ai dati (non approfondiremo le funzionalità di SQL in questa sede). Ognuno di questi comandi può essere eseguito utilizzando la funzione di interfaccia MySqlExecute:


Tipo

Nome

Parametri

Descrizione

bool

MySqlExecute

Questa funzione può essere utilizzata per eseguire istruzioni SQL non SELECT, dopo che la connessione al database è stata stabilita correttamente (utilizzando la funzione MySqlConnect).

In caso di successo di esecuzione del comando, la funzione restituisce true, altrimenti - false. Per i dettagli sull'errore, utilizzare MySQLErrorNumber e MySqlErrorDescription.

int pConnection

Identificatore di connessione

string pQuery

SQL Query

Come una query SQL, puoi anche utilizzare il comando USE per selezionare il database. Vorrei menzionare l'uso di query multi-istruzione. SI tratta di un insieme di comandi SQL separati dal carattere ";".

Per abilitare la modalità multi-istruzioni occorre aprire la connessione al database con il flag CLIENT_MULTI_STATEMENTS:

...
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 questo frammento verranno inserite 3 voci nella tabella EURUSD con una sola chiamata al database. Ciascuna delle query memorizzate nella variabile SQL è separata da ";".

Questo approccio può essere utilizzato per frequenti inserimenti/aggiornamenti/cancellazioni; una serie di comandi necessari viene combinata in un unico "pacchetto", alleviando così il traffico di rete e migliorando le prestazioni del database.

La sintassi INSERT in MySQL è abbastanza ben sviluppata in termini di gestione delle eccezioni.

Ad esempio, se il compito è quello di spostare la cronologia dei prezzi, è necessario creare una tabella per le coppie di valute con una chiave primaria di tipo datetime, poiché la data e l'ora di una barra sono univoche. Inoltre, dovrebbe essere verificato se i dati su una particolare barra esistono nel database (per migliorare la stabilità della migrazione dei dati). Con MySQL questo controllo non è richiesto, poiché l'istruzione INSERT supporta ON DUPLICATE KEY.

In parole più semplici, se si tenta di inserire dati e la tabella ha già una voce con la stessa data e ora, l'istruzione INSERT può essere ignorata o sostituita da UPDATE per questa stessa riga (vedi. http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html).

1.3. Selezione dati

L'istruzione SQL SELECT viene utilizzata per recuperare i dati dal database. La seguente sequenza di azioni viene utilizzata per selezionare i dati e recuperare il risultato della selezione:

  1. Preparazione dell'istruzione SELECT.
  2. Apertura del cursore.
  3. Ottenimento del numero di righe restituite dalla query.
  4. Loop e recupero di ogni riga della query.
  5. Recupero dei dati nelle variabili MQL all'interno del ciclo.
  6. Chiusura del cursore.

Naturalmente, questo è uno schema generale, quindi non tutte le operazioni sono necessarie per ogni caso. Ad esempio, se vuoi assicurarti che esista una riga nella tabella (in base a un criterio definito), sarà sufficiente preparare una query, aprire un cursore, ottenere il numero di righe e chiudere il cursore. In realtà, le fasi obbligatorie sono: preparazione dell'istruzione SELECT, apertura e chiusura del cursore.

Cos'è un cursore? Questo è un riferimento all'area della memoria di contesto, cioè l'insieme di valori di risultato. Quando si invia la query SELECT, il database alloca della memoria per il risultato e crea un puntatore per una riga, che è possibile spostare da una riga all'altra. In questo modo è possibile accedere a tutte le righe nell'ordine di una coda definita dalla query (clausola ORDER BY dell'istruzione SELECT).

Per la selezione dei dati vengono utilizzate le seguenti funzioni dell'interfaccia:

Apertura del cursore:

Tipo

Nome

Parametri

Descrizione

int

MySqlCursorOpen

Questa funzione apre un cursore per la query SELECT e restituisce un identificatore del cursore in caso di successo. In caso contrario, la funzione restituisce "-1". Per scoprire la causa dell'errore, utilizzare le variabili MySQLErrorNumber e MySqlErrorDescription.

int pConnection

Identificatore di connessione con il database

stringa pQuery

Query SQL (l'istruzione SELECT)

Ottenere il numero di righe restituite dalla query:

Tipo

Nome

Parametri

Descrizione

int

MySqlCursorRows

Questa funzione restituisce il numero di righe selezionate dalla query.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

Recupero della riga della query:

Tipo

Nome

Parametri

Descrizione

bool

MySqlCursorFetchRow

Recupera una riga dal set di dati restituito dalla query. Dopo l'esecuzione corretta, è possibile recuperare i dati nelle variabili MQL. La funzione restituisce true in caso di successo, altrimenti restituisce false.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

Recupero dei dati nelle variabili MQL dopo aver recuperato la riga della query:

Tipo

Nome

Parametri

Descrizione

int

MySqlGetFieldAsInt

Questa funzione restituisce la rappresentazione del valore del campo della tabella utilizzando il tipo di dati int.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

int pField

Il numero del campo nell'elenco SELECT (la numerazione inizia con 0)

double

MySqlGetFieldAsDouble

Questa funzione restituisce la rappresentazione del valore del campo della tabella utilizzando il tipo di dati double.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

int pField

Il numero del campo nell'elenco SELECT (la numerazione inizia con 0)

datetime

MySqlGetFieldAsDatetime

Questa funzione restituisce la rappresentazione del valore del campo della tabella utilizzando il tipo di dati datetime.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

int pField

Il numero del campo nell'elenco SELECT (la numerazione inizia con 0)

string

MySqlGetFieldAsString

Questa funzione restituisce la rappresentazione del valore del campo della tabella utilizzando il tipo di dati string.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

int pField

Il numero del campo nell'elenco SELECT (la numerazione inizia con 0)


Tutti i dati restituiti da MySQL hanno una rappresentazione nativa (presentata senza tipi come stringhe).

Pertanto, utilizzando queste funzioni, è possibile trasmettere i dati selezionati al tipo desiderato. L'unico aspetto negativo è la specificazione del numero di colonna (la numerazione inizia da 0) nell'elenco SELECT invece del suo nome. Tuttavia, quando si sviluppa un'applicazione, la preparazione dell'istruzione SELECT e l'ottenimento dei risultati sono quasi sempre in un'unica pagina, quindi è possibile visualizzare la query SELECT, quando si prescrive la logica di recupero dei dati.

Pertanto, bisogna sempre conoscere i numeri dei campi nell'elenco SELECT (questo approccio viene utilizzato anche quando si accede ai dati utilizzando AdoDB). Potremo cambiare questa parte, ma questo avrà un impatto minimo sulla funzionalità della soluzione sviluppata.

Chiudere il cursore:

Tipo

Nome

Parametri

Descrizione

void

MySqlCursorClose

Questa funzione chiude il cursore specificato e libera la memoria.

int pCursorID

L'identificatore del cursore restituito da MySqlCursorOpen

La chiusura di un cursore è un'operazione critica. Non dimenticare di chiudere i cursori.

Immagina di aprire il cursore e dimenticarti di chiuderlo. Supponiamo che i dati vengano recuperati dal cursore ad ogni tick durante la gestione dell'evento OnTick() e ogni volta che viene aperto un nuovo cursore, gli viene allocata memoria (sia sul lato client che sul lato server). Ad un certo punto, il server rifiuterà il servizio perché è stato raggiunto il limite di cursori aperti e ciò potrebbe causare l'overflow del buffer.

Certo, è esagerato, un tale risultato è possibile quando si lavora direttamente con libmysql.dll. Tuttavia, la libreria dinamica MQLMySQL.DLL distribuisce la memoria per i cursori e rifiuterà di aprire un cursore che supera il limite consentito.

Quando si implementano attività reali, è sufficiente mantenere aperti 2-3 cursori. Ogni cursore può gestire una misura cartesiana di dati; l’utilizzo di due o tre cursori contemporaneamente (annidati, ad esempio, quando uno dipende parametricamente da un altro cursore) coprirà due o tre dimensioni. Questo va benissimo per la maggior parte dei compiti. Inoltre, per l'implementazione della selezione di dati complessi, è sempre possibile utilizzare questi oggetti per rappresentare il database (VIEW), crearli lato server e inviare loro query dal codice MQL come tabelle.

1.4. Informazioni aggiuntive

Le seguenti possono essere menzionate come funzionalità aggiuntive:

1.4.1. Lettura di dati da un file .INI

Tipo

Nome

Parametri

Descrizione

String

ReadIni

Restituisce il valore di una chiave della sezione data del file INI.

string pFileName

Il nome del file INI

string pSection

Il nome della sezione

stringa pKey

Il nome della chiave


Spesso memorizzare informazioni sulle connessioni al database (indirizzo IP del server, porta, nome utente, password, ecc.) direttamente nel codice MQL (o parametri dell'Expert Advisor, indicatore di script) non è razionale, perché il server può essere spostato, il suo indirizzo può cambiare dinamicamente, ecc. In questo caso sarà necessario modificare il codice MQL. Pertanto, tutti questi dati dovrebbero essere memorizzati nel file .INI standard, mentre solo il suo nome dovrebbe essere scritto nel programma MQL. Quindi, utilizza la funzione ReadINI per leggere i parametri di connessione e per usarli.

Ad esempio, il file INI contiene le seguenti informazioni:

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

Per ottenere l'indirizzo IP del server, eseguire quanto segue:

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

Il file INI si trova in C:\MetaTrader5\MQL5\Experts e si chiama "MyConnection.ini", si accede alla chiave Server della sezione MYSQL. In un file INI puoi salvare le impostazioni su vari server utilizzati nel tuo progetto.

1.4.2. Stabilire le aree problematiche

Nella libreria di interfaccia c’è la modalità di traccia, che può essere abilitata per il debug di query SQL ovunque in un programma MQL.

Specificare quanto segue nell'area problematica:

SQLTrace = true;

poi

SQLTrace = false;

Se si abilita il tracing all'inizio del programma MQL e poi non lo si disabilita, tutte le chiamate al database verranno registrate. Il registro è conservato nella console del terminale (usando il comando Stampa).


2. Esempi

Questa sezione fornisce alcuni esempi di connessione e utilizzo delle librerie sviluppate. Guardali e valuta l'usabilità della soluzione software.

L'esempio MySQL-003.mq5mostra quanto segue: connessione a un database (i parametri di connessione sono memorizzati nel file .ini), creazione di una tabella, inserimento di dati (anche utilizzando istruzioni multiple) e disconnessione dal database.

//+------------------------------------------------------------------+
//|                                                    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!");
}

L'esempio MySQL-004.mq5 mostra la selezione di dati da una tabella creata dallo script "MySQL-003.mq5". 

//+------------------------------------------------------------------+
//|                                                    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!");
}

Gli esempi precedenti contengono la tipica gestione degli errori utilizzata nei progetti reali.

Infatti, ogni query utilizzata in un programma MQL dovrebbe essere sottoposta a debug in qualsiasi client MySQL (PHPMyAdmin, DB Ninja, console MySQL). Personalmente uso e consiglio un software professionale per lo sviluppo di database come Quest TOAD per MySQL.


Conclusione

Questo articolo non descrive i dettagli dell'implementazione di MQLMySQL.DLL sviluppato nell'ambiente Microsoft Visual Studio 2010 (C/C++). Questa soluzione software è progettata per un uso pratico e ha più di 100 implementazioni di successo in varie aree dello sviluppo del software MQL (dalla creazione di sistemi di trading complessi alla pubblicazione sul web).

  • Di seguito sono allegate le versioni delle librerie per MQL4 e MQL5. Gli allegati includono anche un file zip con il codice sorgente di MQLMySQL.DLL;
  • La documentazione è inclusa negli archivi;
  • Per utilizzare gli esempi, non dimenticare di specificare i parametri di connessione al tuo database nel file \Scripts\MyConnection.ini.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/932

MQL5 Wizard (Procedura guidata): Effettuare ordini, stop-loss e Take Profit su prezzi calcolati. Estensione libreria standard MQL5 Wizard (Procedura guidata): Effettuare ordini, stop-loss e Take Profit su prezzi calcolati. Estensione libreria standard
Questo articolo descrive l'estensione MQL5 Standard Library, che consente di creare Expert Advisor, effettuare ordini, Stop Loss e Take Profit utilizzando la procedura guidata MQL5 dai prezzi ricevuti dai moduli inclusi. Questo approccio non applica ulteriori restrizioni sul numero di moduli e non causa conflitti tra loro quando lavorano congiuntamente.
Costruire una startup di tecnologia sociale, parte I: Twitta i tuoi segnali MetaTrader 5 Costruire una startup di tecnologia sociale, parte I: Twitta i tuoi segnali MetaTrader 5
Oggi impareremo come collegare un terminale MetaTrader 5 con Twitter in modo da poter twittare i segnali di trading dei tuoi EA. Stiamo sviluppando un Social Decision Support System in PHP basato su un servizio web RESTful. Questa idea nasce da una particolare concezione del trading automatico chiamato trading assistito da un computer. Vogliamo che le capacità cognitive dei trader umani filtrino quei segnali di trading che altrimenti verrebbero automaticamente immessi sul mercato dagli Expert Advisor.
Come preparare un Account di trading per la migrazione sull'Hosting Virtuale Come preparare un Account di trading per la migrazione sull'Hosting Virtuale
Il terminale cliente MetaTrader è perfetto per automatizzare le strategie di trading. Possiede tutti gli strumenti necessari per gli sviluppatori di trading robot ‒ potente linguaggio di programmazione MQL4/MQL5 basato su C++, comodo ambiente di sviluppo MetaEditor e tester di strategia multi-thread che supporta il calcolo distribuito in MQL5 Cloud Network. In questo articolo scoprirai come trasferire il tuo terminale cliente verso l’ambiente virtuale mantenendo tutti gli elementi personalizzati.
Racconti di robot di trading: Meno è veramente di più? Racconti di robot di trading: Meno è veramente di più?
Due anni fa in "The Last Crusade" abbiamo esaminato un metodo piuttosto interessante ma attualmente non ampiamente utilizzato per la visualizzazione di informazioni di mercato - grafici a punti e cifre. Ora ti suggerisco di provare a scrivere un robot di trading basato sui modelli rilevati sul grafico a punti e figure.