//+------------------------------------------------------------------+ //| BackChecker.mqh | //| Copyright 2026,Niquel Mendoza. | //| https://www.mql5.com/en/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2026,Niquel Mendoza." #property link "https://www.mql5.com/en/users/nique_372" #property strict #ifndef MQLCYBYLEO_SRC_STEPS_BACKCHECK_MQH #define MQLCYBYLEO_SRC_STEPS_BACKCHECK_MQH //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "..\\BacktestResults\\Base.mqh" #include "..\\Core\\Def.mqh" #include #include #include //+------------------------------------------------------------------+ //| Enums \ Structs \ Interfaces | //+------------------------------------------------------------------+ interface IBackResChecker { // true = paso, false = fallo bool Check(const BackRes& res, string& msg); }; //--- struct MTTesterTaskWithChecker : public MTTesterTask { int checker_idx; }; //--- struct BackCheckerConfig { MTTesterTaskWithChecker backtests[]; IBackResChecker* checkers[]; string expert_path_compile; string expert_path_exe; string file_ea_comunication; string folder_path_comunication_tester; string test_name; long chart_id_orquestador; uint timeout_ms_compile; bool common_folder; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CBackEaChecker : public CLoggerBase { private: //--- MTTesterTaskWithChecker m_backtests_config[]; int m_backtests_config_size; //--- IBackResChecker* m_checkers[]; int m_checkers_size; int m_pos_back_res; // indice actual //--- string m_expert_path_exe; string m_expert_path_compile; string m_file_ea_comunication; bool m_common_folder; string m_folder_path_comunication_tester; const long m_current_chart_id; long m_chart_id_orquestador; string m_test_name; uint m_max_timeout_ms_compile; long m_chart_id_runner; //--- Funciones // compilar bool CompileEa(); // enviar backtest void SendBacktestCommand(); // procesar finish void RecibirEventoFinishBack(const bool exito, const string& sparam_log); public: CBackEaChecker(void) : m_current_chart_id(ChartID()) {} ~CBackEaChecker(void); void Init(BackCheckerConfig& init); void ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CBackEaChecker::~CBackEaChecker(void) { for(int i = 0; i < m_checkers_size; i++) { if(CheckPointer(m_checkers[i]) == POINTER_DYNAMIC) delete m_checkers[i]; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBackEaChecker::Init(BackCheckerConfig& init) { //--- m_expert_path_compile = init.expert_path_compile; m_expert_path_exe = init.expert_path_exe; m_file_ea_comunication = init.file_ea_comunication; m_common_folder = init.common_folder; m_folder_path_comunication_tester = init.folder_path_comunication_tester; m_chart_id_orquestador = init.chart_id_orquestador; m_test_name = init.test_name; m_max_timeout_ms_compile = init.timeout_ms_compile; m_pos_back_res = 0; //--- m_backtests_config_size = ArrayCopyAll(m_backtests_config, init.backtests); m_checkers_size = ArrayCopyAllPtr(m_checkers, init.checkers); m_chart_id_runner = DEFMTTesterGetChartId(); //--- // compilar primero if(!CompileEa()) return; //--- SendBacktestCommand(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CBackEaChecker::CompileEa(void) { string log_path; if(!CompileFileWithLogFile(TERMINAL_MT5_ROOT + m_expert_path_compile, log_path, m_max_timeout_ms_compile)) { LogError(StringFormat("Fallo al compilar: %s", m_expert_path_compile), FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return false; } return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBackEaChecker::SendBacktestCommand(void) { for(int i = 0; i < m_backtests_config_size; i++) { const string full_path = m_folder_path_comunication_tester + StringFormat("data_back_%d.csv", i); m_backtests_config[i].expert_path = m_expert_path_exe; m_backtests_config[i].chart_id_from = m_current_chart_id; //--- // escribe config if(!DEFMMTesterCreateFileWriteData(full_path, m_common_folder, m_backtests_config[i].ToString())) { LogError(StringFormat("Fallo al guardar config: %s", full_path), FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } //--- // envia evento ::ResetLastError(); if(!::EventChartCustom(m_chart_id_runner, DEFMTTESTER_E_ON_TASK, m_current_chart_id, DEFMTTESTER_TO_DBL_ON_TASK(m_common_folder), full_path)) { LogError(StringFormat("Fallo al enviar evento: %s\nError = %d", full_path, ::GetLastError()), FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBackEaChecker::RecibirEventoFinishBack(const bool exito, const string& sparam_log) { //--- if(!exito) { LogError("Fallo al ejecutar backtest", FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } //--- // carga resultado static BackRes res; if(!res.Load(m_file_ea_comunication, m_common_folder)) { LogError("Fallo al cargar BackRes", FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } //--- // valida checker_idx const int idx_checker = m_backtests_config[m_pos_back_res].checker_idx; if(idx_checker < 0 || idx_checker >= m_checkers_size) { LogError(StringFormat("checker_idx invalido = %d", idx_checker), FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } //--- // chekea string msg; if(!m_checkers[idx_checker].Check(res, msg)) { LogError(StringFormat("Check fallo [%d]: %s", m_pos_back_res, msg), FUNCION_ACTUAL); ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name); Remover(); return; } //--- m_pos_back_res++; //--- // todos pasaron if(m_pos_back_res >= m_backtests_config_size) { ::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_SUCCES, 0.00, m_test_name); Remover(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBackEaChecker::ChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { if(id == CHARTEVENT_CUSTOM + DEFMTTESTER_E_FINISH_TASK) { static string arr[2]; if(StringSplit(sparam, DEFMTTESTER_OUT_SPARAM_SEP_U, arr) != DEFMTTESTER_OUT_SPARAM_TOTAL) return; //--- const long chart_id = long(arr[DEFMTTESTER_OUT_SPARAM_IDX_CHARTID]); if(chart_id != m_current_chart_id) return; //--- LogInfo(StringFormat("Evento recibido desde = %I64d | Resultado = %I64d", chart_id, lparam), FUNCION_ACTUAL); RecibirEventoFinishBack((lparam == DEFMTTESTER_TESTER_R_EXITO), arr[DEFMTTESTER_OUT_SPARAM_IDX_LOG]); } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ #endif // MQLCYBYLEO_SRC_STEPS_BACKCHECK_MQH