Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 01): Aufbau der SQLite3-Bibliothek, inspiriert von Python
Inhalt
- Einführung
- Was ist das Modul sqlite3, das in Python angeboten wird?
- Verbinden mit einer SQLite-Datenbank
- Ausführen von SQL-Anweisungen
- Arbeiten mit Text (String)-Werten aus der Datenbank
- Kontrolle von Datenbanktransaktionen
- Andere Methoden im Modul sqlite3
- Protokollierung von Handelsgeschäften in der Datenbank
- Schlussfolgerung
Einführung
Ist es Ihnen schon einmal passiert, dass Sie ein oder zwei Ihrer bevorzugten Module, Bibliotheken, Frameworks usw. in einer anderen Programmiersprache als MQL5 innerhalb von MQL5 haben wollten? Das passiert mir sehr oft.
Es gibt eine große Anzahl von Entwicklern in der MQL5-Gemeinschaft, die aus verschiedenen Programmierbereichen kommen; einige kommen aus der Web-Entwicklung wie ich, andere aus der Android-Entwicklung und viele weitere Programmierbereiche sind heute vertreten. Das bedeutet, dass die meisten Programmierer mit verschiedenen Sprachen wie JavaScript, Java, Python, C++, C#, um nur einige zu nennen, vertraut sind.
In diesen verschiedenen Sprachen stoßen Programmierer auf verschiedene Kodierwerkzeuge (Module), nützliche Module, die wir einfach überall verwenden wollen. Ich verwende zum Beispiel gerne das in Python angebotene NumPy-Modul für mathematische Berechnungen, und zwar so sehr, dass ich eine ähnliche Bibliothek einmal in MQL5, in diesem Artikel, implementieren musste.
Der Versuch, ein Modul, ein Werkzeug, ein Framework usw. von einer Sprache in eine andere – in diesem Fall in MQL5 – zu implementieren, könnte zwar aufgrund der unterschiedlichen Natur der Programmiersprachen zu leicht unterschiedlichen Funktionen und Ergebnissen führen, aber eine ähnliche Syntax oder Erfahrung könnte ausreichen, um die Produktentwicklung in MQL5 für Entwickler, die mit anderen Sprachen vertraut sind, einfach und unterhaltsam zu gestalten. Ganz zu schweigen davon, dass wir dabei vielleicht einige wertvolle Informationen lernen, die unsere Programmierkenntnisse im Allgemeinen festigen könnten.
In dieser neuen Artikelserie werden wir nicht jedes Modul aus anderen Sprachen implementieren, sondern jedes in MQL5 praktische Modul aus einer anderen Sprache. Zum Beispiel Module für mathematische Berechnungen, Datenspeicherung, Datenanalyse usw.
Angefangen mit dem Modul Modulsqlite3, das in der Programmiersprache Python enthalten ist.
Was ist Sqlite3, das in Python angeboten wird?
Lassen Sie uns zunächst eine SQLite-Datenbank verstehen:
Eine SQLite-Datenbank ist eine leichtgewichtige, in sich geschlossene, serverlose SQL-Datenbank-Engine. Es wird häufig in Anwendungen eingesetzt, die eine einfache, eingebettete, lokale Datenspeicherlösung benötigen. Es handelt sich um eine dateibasierte Datenbank, die (Schema, Tabellen, Indizes und Daten) alle in einer einzigen Datei, entweder .sqlite oder .db, speichert.
Im Gegensatz zu den Datenbanken MySQL oder PostgreSQL, die eine gewisse Einrichtung, einen Server und Verwaltungskonfigurationen erfordern, ist SQLite sofort einsatzbereit, da es direkt auf der Festplatte liest und schreibt.
Die Programmiersprache MQL5 verfügt über integrierte Funktionen für die Arbeit mit einer SQLite-Datenbank; diese integrierten Funktionen sind ausreichend. Sie sind jedoch nicht so einfach zu nutzen, wie wenn Sie das sqlite3-Modul in Python verwenden würden.
Wenn Sie zum Beispiel versuchen, eine einfache „Beispiel“-Datenbank zu erstellen und einige Informationen in eine Tabelle mit dem Namen „users“ einzufügen, wird die Tabelle automatisch erstellt, wenn sie nicht existiert.
Verwendung des Python-Moduls sqlite
import sqlite3 conn = sqlite3.connect("example.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, email TEXT UNIQUE ) """) # Execute a query try: cursor.execute("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", ("Bruh", 30, "bruh@example.com")) conn.commit() # commit the transaction | save the new information to a database except sqlite3.DatabaseError as e: print("Insert failed:", e) conn.close() # closing the database
Verwendung nativer MQL5-Funktionen
void OnStart() { //--- int db_handle = DatabaseOpen("example.db", DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE); if (db_handle == INVALID_HANDLE) { printf("Failed to open a database. Error = %s",ErrorDescription(GetLastError())); return; } string sql = " CREATE TABLE IF NOT EXISTS users (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " name TEXT NOT NULL," " age INTEGER," " email TEXT UNIQUE" ")"; if (!DatabaseExecute(db_handle, sql)) //Execute a sql query { printf("Failed to execute a query to the database. Error = %s",ErrorDescription(GetLastError())); return; } if (!DatabaseTransactionBegin(db_handle)) //Begin the transaction { printf("Failed to begin a transaction. Error = %s",ErrorDescription(GetLastError())); return; } sql = "INSERT INTO users (name, age, email) VALUES ('Bruh', 30, 'bruh@example.com')"; if (!DatabaseExecute(db_handle, sql)) //Execute a query { printf("Failed to execute a query to the database. Error = %s",ErrorDescription(GetLastError())); return; } if (!DatabaseTransactionCommit(db_handle)) //Commit the transaction | push the changes to a database { printf("Failed to commit a transaction. Error = %s",ErrorDescription(GetLastError())); return; } DatabaseClose(db_handle); //Close the database }
Wir sind uns alle einig, dass die Verwendung von sqlite3 in Python unseren Code sauberer gemacht hat als die Verwendung nativer MQL5-Funktionen, bei denen wir mit Fehlern, den zurückgegebenen Informationen oder dem Hinzufügen zur Datenbank umgehen müssen.
Das Modul sqlite3 übernimmt viele unnötige Schritte bei der Ausführung eines Befehls und der Verwaltung von Transaktionen, sodass es für Nutzer viel einfacher ist, Daten in die und aus der SQLite-Datenbank zu erhalten und einzufügen, ohne viel Aufwand zu betreiben.
Lassen Sie uns also versuchen, ein ähnliches Modul in MQL5 zu implementieren.
Verbinden mit einer SQLite-Datenbank
Die Methode connect in sqlite3 Python erstellt eine neue Datenbank, wenn der angegebene Name für die Datenbank nicht existiert, und gibt ein Verbindungsobjekt zurück, das die Verbindung zur On-Disk-Datenbank darstellt.
import sqlite3 con = sqlite3.connect("example.db")
In MQL5 ist diese Verbindung dem Datenbank-Handle ähnlich, sodass wir technisch gesehen kein Handle in unserer MQL5-Bibliothek zurückgeben müssen, da wir es in allen Funktionen unserer Klasse verwenden werden.
class CSqlite3 { protected: int m_request; public: int m_db_handle; //... Other functions }
bool CSqlite3::connect(const string db_name, const bool common=false, const bool database_in_memory=false) { int flags = DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE; if (common) //Open the database from the common folder flags |= DATABASE_OPEN_COMMON; if (database_in_memory) //Open the database in memory flags |= DATABASE_OPEN_MEMORY; m_db_handle = DatabaseOpen(db_name, flags); //--- if (m_db_handle == INVALID_HANDLE) { printf("func=%s line=%d, Failed to open a database. Error = %s",__FUNCTION__, __LINE__, ErrorDescription(GetLastError())); return false; } return true; }
MQL5 gibt uns die Möglichkeit, die Daten entweder aus dem Datenpfad des Terminals (Ordner) oder dem Datenpfad Common (Ordner) zu speichern. Wenn das Argument common auf true gesetzt ist , wird die Datenbank aus dem gemeinsamen Datenpfad statt aus einem regulären Datenpfad gelesen.
Wir können dem Nutzer auch die Möglichkeit geben, zu wählen, ob eine Datenbank im Speicher (RAM) oder auf der Festplatte geöffnet werden soll, wenn also die Variable database_in_memory auf true gesetzt ist. Die Datenbank wird im RAM statt auf der Festplatte erstellt und umgekehrt.
Wir ignorieren ein Flag, nämlich DATABASE_OPEN_READONLY, um die Datenbank im Modus ReadOnly zu öffnen. Der Grund dafür ist einfach: Wenn Sie nicht in eine Datenbank schreiben wollen, werden Sie die „INSERT“-Abfragen gar nicht erst ausführen. Macht das Sinn?
Ausführen von SQL-Anweisungen
Dies ist eine der wichtigsten Funktionen, die wir bei der Arbeit mit SQLite-Datenbanken häufig verwenden. Mit dieser Funktion können wir die Informationen abrufen, die Werte in der Datenbank einfügen, aktualisieren, löschen, ändern usw.
Diese Funktion führt SQL-Anweisungen und Befehle direkt in unserer Datenbank aus.
In sqlite3 Python funktioniert die Funktion execute() nahtlos und mühelos. Es kann jeden beliebigen Befehl zum Einfügen oder Abrufen von Informationen entgegennehmen und weiß automatisch, wann und was es zurückgeben soll und was nicht.
In MQL5 haben wir eine eingebaute Funktion namens DatabaseExecute, die ähnlich wie execute() in sqlite3 Python ist. Sie führen beide eine Anfrage an eine bestimmte Datenbank oder Tabelle aus. Diese MQL5-Methode ist jedoch für die Ausführung aller Anfragen geeignet, außer für alle Anfragen mit dem Schlüsselwort „SELECT“ zum Lesen der Informationen aus der Datenbank.
Um effektiv Informationen aus der Datenbank zu lesen, verwenden wir die Funktion DatabasePrepare, da sie ein Handle für eine Anfrage erstellt, die dann mit DatabaseRead ausgeführt werden kann.
Um eine ähnlich flexible Funktion in MQL5 zur Ausführung von Funktionen unabhängig vom Abfragetyp zu erstellen, müssen wir die Typen von SQL-Abfragen unterscheiden und die richtigen Informationen mit der richtigen Abfrage zurückgeben.
execute_res_structure CSqlite3::execute(const string sql) { execute_res_structure res; string trimmed_sql = sql; StringTrimLeft(trimmed_sql); //Trim the leading white space string first_word = trimmed_sql; // Find the index of the first space (to isolate the first SQL keyword) int space_index = StringFind(trimmed_sql, " "); if (space_index != -1) first_word = StringSubstr(trimmed_sql, 0, space_index); // Extract the first word from the query StringToUpper(first_word); //Convert the first word in the query to uppercase for comparison if (first_word == "SELECT") { // SELECT query – prepare and expect data m_request = DatabasePrepare(m_db_handle, sql); res.request = m_request; return res; } else { // INSERT/UPDATE/DELETE – execute directly if (!m_transaction_active) { if (!this.begin()) return res; else m_transaction_active_auto = true; //The transaction was started automatically by the execute function } ResetLastError(); if (!DatabaseExecute(m_db_handle, sql)) { printf("func=%s line=%d, Failed to execute a query to the database. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); } } return res; }
Ähnlich wie bei der Funktion execute in Python wird bei allen Anweisungen, die kein SELECT sind, implizit eine Transaktion mit der Funktion begin eröffnet.
Nachdem Sie die Funktion execute aufgerufen und einige Informationen in die Datenbank eingefügt haben, müssen Sie die Methode commit aufrufen, um die Änderungen in der Datenbank zu speichern. Wir werden diese Funktion später in diesem Beitrag besprechen.
Wenn die Funktion mit einer Anweisung vom Typ Nicht-SELECT oder -Abfrage aufgerufen wird, gibt sie keinen Wert zurück, da sie wahrscheinlich die Informationen in der Datenbank aktualisiert, ändert und einfügt. Wenn die Funktion jedoch mit einer Abfrage vom Typ SELECT aufgerufen wird, gibt sie eine Datenstruktur zurück. Lassen Sie uns diese Struktur im Detail besprechen.
1. Die Methode fetchone
Das in Python angebotene Modul sqlite3 ist in der Lage, dynamisch einen Teil oder die Gesamtheit der von der SQL-Anweisung erhaltenen Informationen zurückzugeben.
Angefangen mit der Möglichkeit, eine Zeile von Informationen aus der Datenbank zurückzugeben.
import sqlite3 conn = sqlite3.connect("example.db") cursor = conn.cursor() print(conn.execute("SELECT * FROM users").fetchone())
Obwohl die SQL-Anweisung alle in der SQLite-Datenbank verfügbaren Informationen anfordert, schränkt die Funktion fetchone die Funktion execute ein, mehr als eine Datenzeile aus der Datenbank zurückzugeben.
Datenbank.

Ausgabe:
(mcm-env) C:\Users\Omega Joctan\OneDrive\Desktop\MCM>C:/Anaconda/envs/mcm-env/python.exe "c:/Users/Omega Joctan/OneDrive/Desktop/MCM/sqlite_test.py" (1, 'Alice', 30, 'alice@example.com')
Wir brauchen eine ähnliche Funktion in unserer MQL5-Klasse, innerhalb der Struktur namens execute_res_structure, die von der Funktion execute zurückgegeben wird.
struct execute_res_structure { int request; CLabelEncoder le; vector fetchone() { int cols = DatabaseColumnsCount(request); vector row = vector::Zeros(cols); while (DatabaseRead(request)) // Essential, read the entire database { string row_string = "("; for (int i = 0; i < cols; i++) { int int_val; //Integer variable double dbl_val; //double string str_val; //string variable ENUM_DATABASE_FIELD_TYPE col_type = DatabaseColumnType(request, i); string col_name; if (!DatabaseColumnName(request, i, col_name)) { printf("func=%s line=%d, Failed to read database column name. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); continue; } switch (col_type) //Detect a column datatype and assign the value read from every row of that column to the suitable variable { case DATABASE_FIELD_TYPE_INTEGER: if (!DatabaseColumnInteger(request, i, int_val)) printf("func=%s line=%d, Failed to read Integer. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%d", int_val); row[i] = int_val; } break; case DATABASE_FIELD_TYPE_FLOAT: if (!DatabaseColumnDouble(request, i, dbl_val)) printf("func=%s line=%d, Failed to read Double. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%.5f", dbl_val); row[i] = dbl_val; } break; case DATABASE_FIELD_TYPE_TEXT: if (!DatabaseColumnText(request, i, str_val)) printf("func=%s line=%d, Failed to read Text. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += "'" + str_val + "'"; row[i] = (double)str_val; } break; default: if (MQLInfoInteger(MQL_DEBUG)) PrintFormat("%s = <Unknown or Unsupported column Type by this Class>", col_name); break; } // Add comma if not last element if (i < cols - 1) row_string += ", "; } row_string += ")"; if (MQLInfoInteger(MQL_DEBUG)) Print(row_string); // Print the full row once break; } DatabaseFinalize(request); return row; // Replace with actual parsed return if needed } //... Other functions }
Aus einer ähnlichen Datenbank in MetaEditor.
Im Folgenden wird eine Abfrage gesendet, die alle in der Datenbanktabelle verfügbaren Informationen anfordert und dann die Menge der zurückgegebenen Informationen auf eine einzige Datenzeile beschränkt.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("example.db"); Print(sqlite3.execute("SELECT * FROM users").fetchone()); sqlite3.close(); }
Ausgabe:
RJ 0 13:25:04.402 sqlite3 test (XAUUSD,H1) (1, 'Alice', 30, 'zero@example.com') DQ 0 13:25:04.402 sqlite3 test (XAUUSD,H1) [1,0,30,0]
Sieht toll aus! Sie können nun mit minimalem Aufwand eine einzelne Zeile mit Werten aus der Datenbank abrufen. Die aktuelle Funktion ignoriert jedoch alle binären Werte und verarbeitet oder kodiert keine Zeichenketten, weshalb alle „TEXT“- oder Zeichenketten-Datentypen in einem zurückgegebenen Vektor auf Null gesetzt wurden.
Im Gegensatz zu Python-Arrays, die Werte verschiedener Datentypen enthalten können, können MQL5-Vektoren und -Arrays dies nicht; wir werden später sehen, wie wir damit umgehen können.
2. Die fetchall-Methode
Im Gegensatz zur Methode fetchone erhält diese Methode alle Informationen, die in der SQL-Anweisung der execute-Methode angefordert werden.
struct execute_res_structure { int request; //... Other functions matrix fetchall() { int cols = DatabaseColumnsCount(request); vector row = vector::Zeros(cols); int CHUNK_SIZE = 1000; //For optimized matrix handling matrix results_matrix = matrix::Zeros(CHUNK_SIZE, cols); int rows_found = 0; //for counting the number of rows seen in the database while (DatabaseRead(request)) // Essential, read the entire database { string row_string = "("; //for printing purposes only. Similar to how Python prints for (int i = 0; i < cols; i++) { int int_val; //Integer variable double dbl_val; //double variable string str_val; //string variable ENUM_DATABASE_FIELD_TYPE col_type = DatabaseColumnType(request, i); string col_name; if (!DatabaseColumnName(request, i, col_name)) { printf("func=%s line=%d, Failed to read database column name. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); continue; } switch (col_type) //Detect a column datatype and assign the value read from every row of that column to the suitable variable { case DATABASE_FIELD_TYPE_INTEGER: if (!DatabaseColumnInteger(request, i, int_val)) printf("func=%s line=%d, Failed to read Integer. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%d", int_val); //For printing purposes only row[i] = int_val; } break; case DATABASE_FIELD_TYPE_FLOAT: if (!DatabaseColumnDouble(request, i, dbl_val)) printf("func=%s line=%d, Failed to read Double. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%.5f",dbl_val); //For printing purposes only row[i] = dbl_val; } break; case DATABASE_FIELD_TYPE_TEXT: if (!DatabaseColumnText(request, i, str_val)) printf("func=%s line=%d, Failed to read Text. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += "'" + str_val + "'"; row[i] = (double)str_val; } break; default: if (MQLInfoInteger(MQL_DEBUG)) PrintFormat("%s = <Unknown or Unsupported column Type by this Class>", col_name); break; } // Add comma if not last element if (i < cols - 1) row_string += ", "; } //--- row_string += ")"; if (MQLInfoInteger(MQL_DEBUG)) Print(row_string); // Print the full row once //--- rows_found++; //Increment the rows counter if (rows_found > (int)results_matrix.Rows()) results_matrix.Resize(results_matrix.Rows()+CHUNK_SIZE, cols); //Resizing the array after 1000 rows results_matrix.Row(row, rows_found-1); //Insert a row into the matrix } results_matrix.Resize(rows_found, cols); //Resize the matrix according to the number of unknown rows found in the database | Final trim DatabaseFinalize(request); //Removes a request created in DatabasePrepare(). return results_matrix; // return the final matrix } //... Other lines of code }
Diesmal geben wir eine Matrix anstelle eines Vektors zurück, um die gesamte Tabelle, die zweidimensional ist, unterbringen zu können.
Der knifflige Teil dieser Funktion besteht darin, die Größe der resultierenden Matrix dynamisch zu verändern. Datenbanktabellen können manchmal riesig sein (mit mehr als 100.000 Zeilen), und es ist schwierig, die Größe der Datenbank oder der Tabellen in Form von Zeilen zu kennen. Die Größenänderung der sich ergebenden Matrix bei jeder Iteration kann diese Funktion also extrem langsam machen, je mehr wir die Zeilen in einer Schleife durchlaufen, denn die Funktion Resize ist eine der rechenintensivsten Funktionen in MQL5.
Aus diesem Grund habe ich mich entschieden, die Größe der Matrix nach jeweils 1000 Iterationen in der obigen Funktion zu ändern, um die Anzahl der Aufrufe der Resize-Methode zu reduzieren.
Verwendung der Funktion.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("example.db"); Print(sqlite3.execute("SELECT * FROM users").fetchall()); sqlite3.close(); }
Ausgaben in der Registerkarte Experten von MetaTrader 5.
IF 0 13:30:33.649 sqlite3 test (XAUUSD,H1) (1, 'Alice', 30, 'zero@example.com') FR 0 13:30:33.649 sqlite3 test (XAUUSD,H1) (2, 'Alice', 30, 'alice@example.com') FD 0 13:30:33.649 sqlite3 test (XAUUSD,H1) (3, 'Alice', 30, 'bro@example.com') QQ 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (4, 'Alice', 30, 'ishowspeed@example.com') MO 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (5, 'Alice', 30, 'damn@example.com') MD 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (6, 'Alice', 30, 'wth@example.com') QN 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (7, 'Bruh', 30, 'stubborn@example.com') NO 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (8, 'Bruh', 30, 'whathehelly@example.com') ED 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (9, 'Bruh', 30, 'huh@example.com') PO 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (10, 'Bruh', 30, 'whatsgoingon@example.com') FS 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (11, 'Bruh', 30, 'bruh@example.com') FF 0 13:30:33.650 sqlite3 test (XAUUSD,H1) (12, 'Bruh', 30, 'how@example.com') JO 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [[1,0,30,0] RG 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [2,0,30,0] QN 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [3,0,30,0] LE 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [4,0,30,0] KL 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [5,0,30,0] NS 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [6,0,30,0] MJ 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [7,0,30,0] HQ 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [8,0,30,0] GH 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [9,0,30,0] OL 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [10,0,30,0] RE 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [11,0,30,0] JS 0 13:30:33.650 sqlite3 test (XAUUSD,H1) [12,0,30,0]]
Alle zwölf in der Datenbank verfügbaren Zeilen wurden von der Ausführungsfunktion zurückgegeben.
Auch hier gilt, dass MQL5-Arrays keine inhomogenen Variablen in einem einzigen Array enthalten können, sodass wir am Ende Nullen für jede Zeile mit Strings aus der Datenbanktabelle haben. Zumindest im Moment.
3. Die Methode fetchmany
Ähnlich wie die Methode fetchall gibt diese Funktion eine Matrix zurück, die Werte aus der Tabelle enthält, aber diese Methode gibt Ihnen die Kontrolle über die Anzahl der Zeilen, die Sie zurückgeben möchten.
struct execute_res_structure { int request; //... other lines of code matrix fetchmany(uint size) { int cols = DatabaseColumnsCount(request); vector row = vector::Zeros(cols); matrix results_matrix = matrix::Zeros(size, cols); int rows_found = 0; while (DatabaseRead(request)) // Essential, read the entire database { string row_string = "("; for (int i = 0; i < cols; i++) { int int_val; //Integer variable double dbl_val; //double variable string str_val; //string variable ENUM_DATABASE_FIELD_TYPE col_type = DatabaseColumnType(request, i); string col_name; if (!DatabaseColumnName(request, i, col_name)) { printf("func=%s line=%d, Failed to read database column name. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); continue; } switch (col_type) //Detect a column datatype and assign the value read from every row of that column to the suitable variable { case DATABASE_FIELD_TYPE_INTEGER: if (!DatabaseColumnInteger(request, i, int_val)) printf("func=%s line=%d, Failed to read Integer. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%d", int_val); row[i] = int_val; } break; case DATABASE_FIELD_TYPE_FLOAT: if (!DatabaseColumnDouble(request, i, dbl_val)) printf("func=%s line=%d, Failed to read Double. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += StringFormat("%.5f", dbl_val); row[i] = dbl_val; } break; case DATABASE_FIELD_TYPE_TEXT: if (!DatabaseColumnText(request, i, str_val)) printf("func=%s line=%d, Failed to read Text. Error = %s", __FUNCTION__, __LINE__, ErrorDescription(GetLastError())); else { row_string += "'" + str_val + "'"; row[i] = (double)str_val; } break; default: if (MQLInfoInteger(MQL_DEBUG)) PrintFormat("%s = <Unknown or Unsupported column Type by this Class>", col_name); break; } } results_matrix.Row(row, rows_found); rows_found++; if (rows_found >= (int)size) break; row_string += ")"; if (MQLInfoInteger(MQL_DEBUG)) Print(row_string); // Print the full row once } results_matrix.Resize(rows_found, cols); //Resize the matrix according to the number of unknown rows found in the database | Final trim DatabaseFinalize(request); //Removes a request created in DatabasePrepare(). return results_matrix; // return the final matrix } //... other functions }
Verwendung der Funktion.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("example.db"); Print(sqlite3.execute("SELECT * FROM users").fetchmany(5)); sqlite3.close(); }
Ausgabe:
EG 0 13:45:25.480 sqlite3 test (XAUUSD,H1) (1'Alice'30'zero@example.com') IR 0 13:45:25.481 sqlite3 test (XAUUSD,H1) (2'Alice'30'alice@example.com') EJ 0 13:45:25.481 sqlite3 test (XAUUSD,H1) (3'Alice'30'bro@example.com') NS 0 13:45:25.481 sqlite3 test (XAUUSD,H1) (4'Alice'30'ishowspeed@example.com') CD 0 13:45:25.481 sqlite3 test (XAUUSD,H1) [[1,0,30,0] KR 0 13:45:25.481 sqlite3 test (XAUUSD,H1) [2,0,30,0] LI 0 13:45:25.481 sqlite3 test (XAUUSD,H1) [3,0,30,0] QP 0 13:45:25.481 sqlite3 test (XAUUSD,H1) [4,0,30,0] EJ 0 13:45:25.481 sqlite3 test (XAUUSD,H1) [5,0,30,0]]
Obwohl diese drei Funktionen in der Lage sind, die Menge der in einer Matrix zu speichernden Daten zu steuern, sind sie immer noch von Ihrer SQL-Anweisung abhängig; sie ist diejenige, die in erster Linie steuert, welche Informationen zurückgegeben werden sollen. Die oben genannten 3 Funktionen sind nur Gateways für den Empfang der von der SQL-Anweisung angeforderten Daten.
4. Zusätzliche Methode, Prüfung des Status der Ausführungsfunktion
Standardmäßig gibt diese Funktion eine Struktur mit Funktionen zurück, die für die Rückgabe der nach einer SQL-Anweisung vom Typ „SELECT“ erhaltenen Daten nützlich sind. Allerdings kann es vorkommen, dass die Execute-Methode für eine Nicht-SELECT-Anweisung in SQL aufgerufen wird, die keine Auswahl ist, sodass diese Funktion keine Daten zurückgibt, die zur Überprüfung des Erfolgs verwendet werden könnten.
Um dies zu erreichen, müssen wir die Variable boolean in der Struktur verwenden, die von der Funktion execute zurückgegeben wird.
{
sqlite3.connect("indicators.db");
sqlite3.execute(
" CREATE TABLE IF NOT EXISTS EURUSD ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" example_indicator FLOAT,"
")"
);
if (!sqlite3.execute(StringFormat("INSERT INTO USDJPY (example_indicator) VALUES (%.5f)",(double)rand())).boolean) //A SQL query with a purposefully placed error
printf("The execute function failed!");
}Die boolesche Variable ist eine Variable vom Typ bool, sie wird wahr, wenn die Funktion erfolgreich war, und falsch, wenn sie fehlgeschlagen ist.
Ausgabe:
QK 2 18:27:00.575 sqlite3 test (XAUUSD,H1) database error, near ")": syntax error DN 0 18:27:00.576 sqlite3 test (XAUUSD,H1) func=CSqlite3::execute line=402, Failed to execute a query to the database. Error = Generic database error GE 2 18:27:00.578 sqlite3 test (XAUUSD,H1) database error, no such table: USDJPY PS 0 18:27:00.578 sqlite3 test (XAUUSD,H1) func=CSqlite3::execute line=402, Failed to execute a query to the database. Error = Generic database error IS 0 18:27:00.578 sqlite3 test (XAUUSD,H1) The execute function failed!
Um zu überprüfen, ob die Funktion erfolgreich war, wenn sie mit einer „SELECT“-Abfrage gegeben wird, die einige Daten zurückgibt, müssen Sie die Größe der zurückgegebenen Matrix oder des Vektors bewerten.
Wenn sie leer sind (rows==0 für eine Matrix und size==0 für einen Vektor), bedeutet dies, dass die Ausführungsfunktion fehlgeschlagen ist.
Arbeiten mit Text (strings) aus der Datenbank
Wie im vorangegangenen Abschnitt erläutert, ist die vereinfachte Ausführungsfunktion nicht in der Lage, String-Werte in einer Matrix oder einem Vektor zurückzugeben, und das wissen wir; Strings oder String-Werte können genauso nützlich sein wie andere Variablen, sodass Sie, nachdem Sie alle in einer Matrix gespeicherten Werte aus der Datenbank erhalten haben, alle Spalten mit String-Werten separat extrahieren müssen.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("example.db"); Print("database matrix:\n",sqlite3.execute("SELECT * FROM users").fetchall()); string name_col[]; sqlite3.execute("SELECT name FROM users").fetch_column("name", name_col); ArrayPrint(name_col); }
Ausgehend von diesem Array von String-Werten können Sie Wege finden, sie in Variablen zu kodieren, die von der Matrix akzeptiert werden (double, float usw.), und sie wieder in die Matrix einzufügen.
Ausgabe:
LS 0 12:48:12.456 sqlite3 test (XAUUSD,H1) (1, 'Alice', 30, 'zero@example.com')
KG 0 12:48:12.456 sqlite3 test (XAUUSD,H1) (2, 'Alice', 30, 'alice@example.com')
KH 0 12:48:12.456 sqlite3 test (XAUUSD,H1) (3, 'Alice', 30, 'bro@example.com')
OM 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (4, 'Alice', 30, 'ishowspeed@example.com')
GH 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (5, 'Alice', 30, 'damn@example.com')
KH 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (6, 'Alice', 30, 'wth@example.com')
OR 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (7, 'Bruh', 30, 'stubborn@example.com')
LJ 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (8, 'Bruh', 30, 'whathehelly@example.com')
OG 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (9, 'Bruh', 30, 'huh@example.com')
RS 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (10, 'Bruh', 30, 'whatsgoingon@example.com')
PF 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (11, 'Bruh', 30, 'bruh@example.com')
DS 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (12, 'Bruh', 30, 'how@example.com')
NL 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (13, 'John', 83, 'johndoe@example.com')
IE 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (14, 'John', 83, 'johndoe2@example.com')
KP 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (15, 'John', 83, 'johndoe3@example.com')
IN 0 12:48:12.457 sqlite3 test (XAUUSD,H1) (16, 'John', 83, 'johndoe4@example.com')
KD 0 12:48:12.457 sqlite3 test (XAUUSD,H1) database matrix:
HP 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [[1,0,30,0]
PF 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [2,0,30,0]
OM 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [3,0,30,0]
ND 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [4,0,30,0]
MK 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [5,0,30,0]
LR 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [6,0,30,0]
KI 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [7,0,30,0]
JP 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [8,0,30,0]
IG 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [9,0,30,0]
QM 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [10,0,30,0]
LF 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [11,0,30,0]
KO 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [12,0,30,0]
RP 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [13,0,83,0]
MI 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [14,0,83,0]
PR 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [15,0,83,0]
DF 0 12:48:12.457 sqlite3 test (XAUUSD,H1) [16,0,83,0]]
LQ 0 12:48:12.458 sqlite3 test (XAUUSD,H1) "Alice" "Alice" "Alice" "Alice" "Alice" "Alice" "Bruh" "Bruh" "Bruh" "Bruh" "Bruh" "Bruh" "John" "John" "John" "John" Steuerung von Datenbanktransaktionen
Das Modul sqlite3 in Python bietet mehrere Methoden zur Steuerung, ob, wann und wie Datenbanktransaktionen geöffnet und geschlossen werden. Wir können uns in unserer Klasse an das gleiche Format anpassen, was die Handhabung von Transaktionen bei der Arbeit mit SQLite-Datenbanken erleichtert.
| Funktion | Beschreibung | Hinweis |
|---|---|---|
bool CSqlite3::commit(void) | Bestätigt die aktuelle Transaktion und macht alle Änderungen an der Datenbank dauerhaft. | Sie ist nach den SQL-Abfragetypen INSERT, UPDATE oder DELETE erforderlich, wenn autocommit auf false gesetzt ist. |
bool CSqlite3::rollback(void) | Macht die aktuelle Transaktion rückgängig, wobei alle nicht bestätigten Änderungen rückgängig gemacht werden. | Nützlich für die Fehlerbehandlung. |
bool CSqlite3::begin(void) | Beginnt die Transaktion, die später in die Datenbank übertragen wird. | Bevor Änderungen an der Datenbank vorgenommen werden, wird eine explizitere Syntax verwendet. Die Funktion execute() ruft sie automatisch auf, wenn autocommit auf true gesetzt ist. |
bool CSqlite3::in_transaction() | Eine boolesche Funktion zur Überprüfung, ob die Transaktion aktiv ist oder nicht. | Sie gibt true zurück, wenn die Transaktion gerade aktiv ist. |
CSqlite3(bool autocommit=false) | Optional – nicht empfohlen – können Sie den Wert autocommit innerhalb eines Klassenkonstruktors ändern, um das automatische Commit der Transaktion innerhalb der Ausführungsfunktion zu aktivieren oder zu deaktivieren. |
Die drei Funktionen (begin, rollback und commit) bauen auf den in MQL5 eingebauten Funktionen zur Behandlung von Datenbanktransaktionen auf.
bool CSqlite3::commit(void) { if (!DatabaseTransactionCommit(m_db_handle)) { printf("func=%s line=%d, Failed to commit a transaction. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); return false; } m_transaction_active = false; //Reset the transaction after commit m_transaction_active_auto = false; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSqlite3::begin(void) { if (m_transaction_active) { if (!m_transaction_active_auto) //print only if the user started the transaction not when it was started automatically by the execute() function printf("Can not begin, already in a transaction. Call the function rollback() to disregard it, or commit() to save the changes"); return false; } //--- if (!DatabaseTransactionBegin(m_db_handle)) { printf("func=%s line=%d, Failed to begin a transaction. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); return false; } m_transaction_active = true; m_transaction_active_auto = false; return m_transaction_active; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSqlite3::rollback(void) { if (!DatabaseTransactionRollback(m_db_handle)) { printf("func=%s line=%d, Failed to rollback a transaction. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); return false; } m_transaction_active = false; //Reset the transaction after rollback m_transaction_active_auto = false; return true; }
Andere Methoden im Modul sqlite3
Diese Funktionen dieses Moduls werden zwar weniger häufig verwendet, sind aber dennoch für verschiedene Aufgaben nützlich.
1. Die Methode executemany
Mit dieser Funktion können Sie mit einem einzigen Funktionsaufruf viele Zeilen in eine Datenbanktabelle einfügen.
Angenommen, Sie haben eine Matrix mit mehreren Zeilen, wobei jede Spalte eine bestimmte Art von Indikatorwerten enthält, die Sie auf einmal in die Datenbank einfügen möchten – dies ist eine „goto“-Funktion.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("indicators.db"); sqlite3.execute( " CREATE TABLE IF NOT EXISTS EURUSD (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " INDICATOR01 FLOAT," " INDICATOR02 FLOAT," " INDICATOR03 FLOAT" ")" ); matrix data = {{101, 25, 001}, {102, 32, 002}, {103, 29, 003}}; sqlite3.executemany("INSERT INTO EURUSD (INDICATOR01, INDICATOR02, INDICATOR03) VALUES (?,?,?)", data); sqlite3.commit(); sqlite3.close(); }
Ausgabe:

Die Anzahl der Fragezeichen nach dem Schlüsselwort VALUES muss gleich der Anzahl der Spalten in der params-Matrix sein, damit diese Funktion fehlerfrei funktioniert.
Beachten Sie, dass Sie aufgrund von Matrixbeschränkungen auf homogene Variablen innerhalb einer einzigen Matrix mit dieser Funktion nicht gleichzeitig verschiedene Datentypen zu Ihrer Datenbank hinzufügen können, z. B.. Sie können nicht gleichzeitig eine Zeichenkette und eine Doppelspalte in die Datenbank einfügen.
2. Die Methode executescript
Diese Funktion ist praktisch, wenn Sie mehrere SQL-Anweisungen in einem Durchgang ausführen möchten. Erstellen Sie zum Beispiel Tabellen, fügen Sie Zeilen ein usw. in einer einzigen Anweisung. Die wichtigsten Merkmale dieser Funktion sind folgende.
void OnStart() { //--- sqlite3.connect("indicators.db"); // Use executescript to log actions sqlite3.executescript( "CREATE TABLE IF NOT EXISTS logs (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "event TEXT NOT NULL" ");" "INSERT INTO logs (event) VALUES ('Users batch inserted');" ); sqlite3.close(); }
- Sie nimmt eine Zeichenkette aus mehreren SQL-Anweisungen, die durch Semikolons „;“ getrennt sind.
- Sie führt sie alle auf einmal aus, nicht parametrisiert und nicht geparst wie execute() oder executemany().
- Sie gibt keine Ergebnisse zurück, sondern führt nur die Befehlsgruppe aus.
Ausgabe:

Die Funktion executescript überträgt automatisch alle offenen Transaktionen, sodass Sie die Funktion commit in der Regel nicht explizit aufrufen müssen, es sei denn, dies ist zur Kontrolle erforderlich.
3. Zusätzliche Methode, die print_table Methode
Um die Funktionalität ähnlich wie cursor.description des sqlite3-Moduls in Python zu erhalten, das die Struktur einer SQL-Abfrage, einer Datenbank oder einer Tabelle zurückgibt.
Wir können die in MQL5 integrierte Funktion DatabasePrint verwenden, um ein ähnliches Ergebnis zu erzielen.
void CSqlite3::print_table(const string table_name_or_sql, const int flags=0) // Prints a table or an SQL request execution result in the Experts journal. { if (DatabasePrint(m_db_handle, table_name_or_sql, flags)<0) printf("func=%s line=%d, Failed to print the table or query result. Error = %s",__FUNCTION__,__LINE__, ErrorDescription(GetLastError())); }
Verwendung:
sqlite3.print_table("SELECT * FROM users");Ausgabe:
CM 0 13:17:19.028 sqlite3 test (XAUUSD,H1) #| id name age email PJ 0 13:17:19.028 sqlite3 test (XAUUSD,H1) --+-------------------------------------- MH 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 1| 1 Alice 30 zero@example.com KS 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 2| 2 Alice 30 alice@example.com NO 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 3| 3 Alice 30 bro@example.com NI 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 4| 4 Alice 30 ishowspeed@example.com IL 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 5| 5 Alice 30 damn@example.com LO 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 6| 6 Alice 30 wth@example.com EE 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 7| 7 Bruh 30 stubborn@example.com EM 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 8| 8 Bruh 30 whathehelly@example.com ER 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 9| 9 Bruh 30 huh@example.com HK 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 10| 10 Bruh 30 whatsgoingon@example.com IQ 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 11| 11 Bruh 30 bruh@example.com LQ 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 12| 12 Bruh 30 how@example.com GG 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 13| 13 John 83 johndoe@example.com GK 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 14| 14 John 83 johndoe2@example.com NQ 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 15| 15 John 83 johndoe3@example.com QF 0 13:17:19.028 sqlite3 test (XAUUSD,H1) 16| 16 John 83 johndoe4@example.com
Protokollierung von Handelsgeschäften in der Datenbank
Wir haben einige einfache Beispiele gesehen, wie Sie SQL-Anweisungen ausführen, Werte in die Datenbank eingeben und vieles mehr. Tun wir endlich etwas Sinnvolles; wir fügen alle Handelsgeschäfte in der MetaTrader 5 Historie in eine SQLite Datenbank ein.
Dieses Beispiel stammt aus diesem Artikel.
Ohne sqlite3.
//--- auxiliary variables ulong deal_ticket; // deal ticket long order_ticket; // a ticket of an order a deal was executed by long position_ticket; // ID of a position a deal belongs to datetime time; // deal execution time long type ; // deal type long entry ; // deal direction string symbol; // a symbol a deal was executed for double volume; // operation volume double price; // price double profit; // financial result double swap; // swap double commission; // commission long magic; // Magic number (Expert Advisor ID) long reason; // deal execution reason or source //--- go through all deals and add them to the database bool failed=false; int deals=HistoryDealsTotal(); // --- lock the database before executing transactions DatabaseTransactionBegin(database); for(int i=0; i<deals; i++) { deal_ticket= HistoryDealGetTicket(i); order_ticket= HistoryDealGetInteger(deal_ticket, DEAL_ORDER); position_ticket=HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID); time= (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME); type= HistoryDealGetInteger(deal_ticket, DEAL_TYPE); entry= HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); symbol= HistoryDealGetString(deal_ticket, DEAL_SYMBOL); volume= HistoryDealGetDouble(deal_ticket, DEAL_VOLUME); price= HistoryDealGetDouble(deal_ticket, DEAL_PRICE); profit= HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); swap= HistoryDealGetDouble(deal_ticket, DEAL_SWAP); commission= HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); magic= HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); reason= HistoryDealGetInteger(deal_ticket, DEAL_REASON); //--- add each deal to the table using the following query string request_text=StringFormat("INSERT INTO DEALS (ID,ORDER_ID,POSITION_ID,TIME,TYPE,ENTRY,SYMBOL,VOLUME,PRICE,PROFIT,SWAP,COMMISSION,MAGIC,REASON)" "VALUES (%d, %d, %d, %d, %d, %d, '%s', %G, %G, %G, %G, %G, %d, %d)", deal_ticket, order_ticket, position_ticket, time, type, entry, symbol, volume, price, profit, swap, commission, magic, reason); if(!DatabaseExecute(database, request_text)) { PrintFormat("%s: failed to insert deal #%d with code %d", __FUNCTION__, deal_ticket, GetLastError()); PrintFormat("i=%d: deal #%d %s", i, deal_ticket, symbol); failed=true; break; } } //--- check for transaction execution errors if(failed) { //--- roll back all transactions and unlock the database DatabaseTransactionRollback(database); PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError()); return(false); } //--- all transactions have been performed successfully - record changes and unlock the database DatabaseTransactionCommit(database);
Mit sqlite3.
#include <sqlite3.mqh> CSqlite3 sqlite3; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- sqlite3.connect("Trades_database.db"); //--- auxiliary variables ulong deal_ticket; // deal ticket long order_ticket; // a ticket of an order a deal was executed by long position_ticket; // ID of a position a deal belongs to datetime time; // deal execution time long type ; // deal type long entry ; // deal direction string symbol; // a symbol a deal was executed for double volume; // operation volume double price; // price double profit; // financial result double swap; // swap double commission; // commission long magic; // Magic number (Expert Advisor ID) long reason; // deal execution reason or source //--- go through all deals and add them to the database HistorySelect(0, TimeCurrent()); int deals=HistoryDealsTotal(); sqlite3.execute("CREATE TABLE IF NOT EXISTS DEALS (" "ID INT KEY NOT NULL," "ORDER_ID INT NOT NULL," "POSITION_ID INT NOT NULL," "TIME INT NOT NULL," "TYPE INT NOT NULL," "ENTRY INT NOT NULL," "SYMBOL CHAR(10)," "VOLUME REAL," "PRICE REAL," "PROFIT REAL," "SWAP REAL," "COMMISSION REAL," "MAGIC INT," "REASON INT );" ); //Creates a table if it doesn't exist sqlite3.begin(); //Start the transaction // --- lock the database before executing transactions for(int i=0; i<deals; i++) //loop through all deals { deal_ticket= HistoryDealGetTicket(i); order_ticket= HistoryDealGetInteger(deal_ticket, DEAL_ORDER); position_ticket=HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID); time= (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME); type= HistoryDealGetInteger(deal_ticket, DEAL_TYPE); entry= HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); symbol= HistoryDealGetString(deal_ticket, DEAL_SYMBOL); volume= HistoryDealGetDouble(deal_ticket, DEAL_VOLUME); price= HistoryDealGetDouble(deal_ticket, DEAL_PRICE); profit= HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); swap= HistoryDealGetDouble(deal_ticket, DEAL_SWAP); commission= HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); magic= HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); reason= HistoryDealGetInteger(deal_ticket, DEAL_REASON); //--- add each deal to the table using the following query string request_text=StringFormat("INSERT INTO DEALS (ID,ORDER_ID,POSITION_ID,TIME,TYPE,ENTRY,SYMBOL,VOLUME,PRICE,PROFIT,SWAP,COMMISSION,MAGIC,REASON)" "VALUES (%d, %d, %d, %d, %d, %d, '%s', %G, %G, %G, %G, %G, %d, %d)", deal_ticket, order_ticket, position_ticket, time, type, entry, symbol, volume, price, profit, swap, commission, magic, reason); sqlite3.execute(request_text); } sqlite3.commit(); //Commit all deals to the database at once sqlite3.close(); //close the database }
Das Ergebnis.

Abschließende Überlegungen
Die Nachbildung des sqlite3-Moduls von Python in MQL5 war eine lohnende Herausforderung, die sowohl die Flexibilität als auch die Grenzen von MQL5 im Vergleich zu Python aufzeigt. Während MQL5 die eingebaute Unterstützung für fortgeschrittene Funktionen wie Autorisierer oder Kontextmanager für eine SQLite-Datenbank fehlt, kann man mit sorgfältiger Abstraktion und Methodenentwurf eine sehr ähnliche Entwicklererfahrung erreichen.
Diese nutzerdefinierte Klasse CSqlite3 ermöglicht es MQL5-Entwicklern nun, mit SQLite-Datenbanken auf strukturierte, auf Python-Art zu interagieren – komplett mit Unterstützung für Abfragen, Transaktionen, Commit/Rollback-Kontrolle und Fetch-Operationen wie fetchone(), fetchmany(), fetchall(), etc.
Wenn Sie von Python kommen, sollte Ihnen dieses Modul vertraut vorkommen und hoffentlich auch Spaß machen.
Peace out.
Tabelle der Anhänge
| Dateiname | Beschreibung und Verwendung |
|---|---|
| Include\errordescription.mqh | Enthält Beschreibungen aller von MQL5 und MetaTrader 5 erzeugten Fehlercodes |
| Include\sqlite3.mqh | Enthält die Klasse CSqlite3 für die Arbeit mit SQLite-Datenbanken in einer Python-ähnlichen Weise. |
| Scripts\sqlite3 test.mq5 | Ein Skript zum Testen der Klasse CSqlite3. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18640
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 72): Verwendung der Muster von MACD und OBV mit überwachtem Lernen
Beherrschung von Protokollaufzeichnungen (Teil 9): Implementierung des Builder-Musters und Hinzufügen von Standardkonfigurationen
Automatisieren von Handelsstrategien in MQL5 (Teil 21): Verbesserung des Handels mit neuronalen Netzen durch adaptive Lernraten
Senden von Nachrichten von MQL5 an Discord, Erstellen eines Discord-Bots für MetaTrader 5
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.

