MqlCIByLeo/Src/Steps/BackChek.mqh

270 lines
9.7 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <TSN\\ExtraCodes\\RunnerProtDef.mqh>
#include <TSN\\MQLArticles\\Utils\\Basic.mqh>
#include <TSN\\ExtraCodes\\Func.mqh>
//+------------------------------------------------------------------+
//| 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