2026-04-12 13:31:09 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| 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
|