//+------------------------------------------------------------------+ //| MultiBen.mqh | //| Copyright 2026, Niquel Mendoza | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza" #property link "https://www.mql5.com" #property strict #ifndef MQLARTICLES_UTILS_MBEN_BASIC_MQH #define MQLARTICLES_UTILS_MBEN_BASIC_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\FA\\LoggerDef.mqh" //#define MQLARTICLES_MBEN_ENABLE_FINAL_FLUSH #ifdef MQLARTICLES_MBEN_ENABLE_FINAL_FLUSH #include #include #endif // MQLARTICLES_MBEN_ENABLE_FINAL_FLUSH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #resource "sql_init.sql" as const string g_mqlarticles_mben_sql_init #resource "sql_insert.sql" as const string g_mqlarticles_mben_sql_insert //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #ifndef MQLARTICLES_MULTIBEN_DATA_SIZE #define MQLARTICLES_MULTIBEN_DATA_SIZE 10 #endif // MQLARTICLES_MULTIBEN_DATA_SIZE //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CMultiBenchmark { public: CMultiBenchmark(void) {} ~CMultiBenchmark(void) {} //---- Data struct TempData { string op; string dataset; int iter; ulong total; }; static TempData s_data[MQLARTICLES_MULTIBEN_DATA_SIZE]; static int s_pos; //--- static void Add( const string& op, const string& dataset, const int iter, const ulong elapsed); //--- // Luego de aƱadir todo lo finaliza y flushea a la .db static void Finish(const string& name, const string& run_date, const string& lib_name); //--- // Esto es especifico en caso se este revisando un .log // Esto lo que hara es parsear recorrer linea por linea // Y va ir flusheando datos a la .db // siguienteod un formato especial // el print empeiza con [MultiBen& // Inicio: "[MultiBen& %[NameLib]"" // Datos: "@VALOR" // Fin: "[MultiBen& $[Tag|DataSet||LibName|Iter]" static void ParseLogFileTodayFlushDb(const string& name); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CMultiBenchmark::s_pos = 0; CMultiBenchmark::TempData CMultiBenchmark::s_data[MQLARTICLES_MULTIBEN_DATA_SIZE]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // elaped=tiempo tardado // compeditor=nombre del competidor // op=operacion (parse, etc) // dataset=nombre del dataset static void CMultiBenchmark::Add( const string& op, const string& dataset, const int iter, const ulong elapsed) { //--- ejecutamos s_data[s_pos].op = op; s_data[s_pos].dataset = dataset; s_data[s_pos].iter = iter; s_data[s_pos].total = elapsed; s_pos++; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CMultiBenchmark::Finish(const string& name, const string& run_date, const string& lib_name) { //--- // solo imprimimos const int sql_handle = DatabaseOpen(name, DATABASE_OPEN_COMMON | DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE); if(sql_handle == INVALID_HANDLE) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al abrir la db [%s], last err = %d", name, ::GetLastError())); return; } //--- creamos si no existe if(!DatabaseExecute(sql_handle, g_mqlarticles_mben_sql_init)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al crear la tabla db, last err = %d", ::GetLastError())); return; } //--- Iniciamos ::ResetLastError(); if(!DatabaseTransactionBegin(sql_handle)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al inicar transacion datos al m_sql_handle: ", ::GetLastError())); return; } //--- ::ResetLastError(); const int stmt = DatabasePrepare(sql_handle, g_mqlarticles_mben_sql_insert); if(stmt == INVALID_HANDLE) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al ejeuctar un preprare de la base de datos, ultimo err = %d", ::GetLastError())); return; } //--- for(int i = 0; i < s_pos; i++) { DatabaseReset(stmt); DatabaseBind(stmt, 0, run_date); DatabaseBind(stmt, 1, s_data[i].op); DatabaseBind(stmt, 2, s_data[i].dataset); DatabaseBind(stmt, 3, lib_name); DatabaseBind(stmt, 4, s_data[i].iter); DatabaseBind(stmt, 5, s_data[i].total); DatabaseRead(stmt); } //--- DatabaseFinalize(stmt); //--- ::ResetLastError(); if(!DatabaseTransactionCommit(sql_handle)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al enviar datos al m_sql_handle: ", ::GetLastError())); return; } //--- DatabaseClose(sql_handle); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ static void CMultiBenchmark::ParseLogFileTodayFlushDb(const string& name) { // Bueno por ahora hagamos algo el .log del archivo se lo dan a la ia // y le dicen: /* Hola, me ayudas con algo? Te dare un .log de registros de una terminal de MT5 en la cual estuve cooriendo benchamarks necesito que tomes ese .log y recolectes datos. La idea es la sigueinte 1. Lee de arriba a abajo el .log, por cada linea haz esto: { En caso estes en un ciclo: { Haz los dos A. { 1. Indentifica los mensajes que contenga el patron "[MultiBen&" inicialmente, si no existe ve al siguiente bloque B 2. Luego busca el carcater % si aparece otro carcater ve a B 3. En caso encuentres % - Significa un "final" de "ciclo", por lo que obtendras el string que esta dentro de los corchetes [] Y buscaras en tus registros ese mismo string (comparas contra lib_name) En caso lo encuntres eso es un dato completo lo apeneas al struct final (que harcodearas en MQL5) Luego de esto lo guardas... y buscaras devuelta el patron "[MultiBen&" pero con % como inicio } B. { 1. verifica uqe la linea empieze con @, si no vuelve a la siguiente linea luego captura el valor numerico.. y ve actulizo tus registros con el menor de todos (aveces por ciclo hay varios.. la idea es quedarse con el menor valor de todos) } } Si no lo estas { 1. Indentifica los mensajes que contenga el patron "[MultiBen&" inicialmente, ahora si no existe saltate al paso 4: 2. Luego busca el carcater $ si aparece otro carcater descarta esa linea En caso encuentres - Se inicia un ciclo por lo que recolectaras con un split ("|") esos 4 valores los guardas tal como posiciones: 0: tag 1: dataset 2: lib_name 3: iteraciones Ahora ya no se podra entrar a esta seccion } Eso es todo.. esto lo reptires hasta terminar con el archivo En la segunda etapa emepzaras a crear el script para MT5, Te dare como base esto: { const int sql_handle = DatabaseOpen(name, DATABASE_OPEN_COMMON | DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE); if(sql_handle == INVALID_HANDLE) { FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al abrir la db [%s], last err = %d", name, ::GetLastError())); return; } //--- creamos si no existe if(!DatabaseExecute(sql_handle, g_mqlarticles_mben_sql_init)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al crear la tabla db, last err = %d", ::GetLastError())); return; } //--- Iniciamos ::ResetLastError(); if(!DatabaseTransactionBegin(sql_handle)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al inicar transacion datos al m_sql_handle: ", ::GetLastError())); return; } //--- ::ResetLastError(); const int stmt = DatabasePrepare(sql_handle, g_mqlarticles_mben_sql_insert); if(stmt == INVALID_HANDLE) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al ejeuctar un preprare de la base de datos, ultimo err = %d", ::GetLastError())); return; } //--- const string run_date = TimeToString(TimeLocal(),TIME_DATE); for(int i = 0; i < s_pos; i++) { DatabaseReset(stmt); DatabaseBind(stmt, 0, run_date); DatabaseBind(stmt, 1, s_data[i].op); DatabaseBind(stmt, 2, s_data[i].dataset); DatabaseBind(stmt, 3, s_data[i].lib_name); DatabaseBind(stmt, 4, s_data[i].iter); DatabaseBind(stmt, 5, s_data[i].total); DatabaseRead(stmt); } //--- DatabaseFinalize(stmt); //--- ::ResetLastError(); if(!DatabaseTransactionCommit(sql_handle)) { DatabaseClose(sql_handle); FastLog(FUNCION_ACTUAL, FATAL_ERROR_TEXT, StringFormat("Fallo al enviar datos al m_sql_handle: ", ::GetLastError())); return; } //--- DatabaseClose(sql_handle); } Lo que tu cambiarsae sera justo la parte de los datos cuando se itera agrango a la base ahi en vez de s_data pones la struct que hacodeaste (iteras sobre ella) y listo eso es todo por cierto: g_mqlarticles_mben_sql_init = { CREATE TABLE IF NOT EXISTS timing_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_date TEXT, operation TEXT, dataset TEXT, library TEXT, iteration INTEGER, microseconds INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(run_date, library, operation, dataset, iteration) ); } g_mqlarticles_mben_sql_insert = { INSERT INTO timing_results (run_date, operation, dataset, library, iteration, microseconds) VALUES (?,?,?,?,?,?) ON CONFLICT(run_date, library, operation, dataset, iteration) DO UPDATE SET microseconds = excluded.microseconds WHERE excluded.microseconds < timing_results.microseconds; } Vamos ayudame con eso. */ // En caso hayan hecho algo mal le emncionan que agtege ciertos explicitamente.. } //--- } //+------------------------------------------------------------------+ #endif // MQLARTICLES_UTILS_MBEN_BASIC_MQH