338 lines
12 KiB
MQL5
338 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| BackComparer.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_BACKCOMPARER_MQH
|
|
#define MQLCYBYLEO_SRC_STEPS_BACKCOMPARER_MQH
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Include |
|
|
//+------------------------------------------------------------------+
|
|
//--- Resultados
|
|
#include "..\\BacktestResults\\Base.mqh"
|
|
//--- Procolo de comunicaion con el orquestaodr del CI
|
|
#include "..\\Core\\Def.mqh"
|
|
//--- Procolo de comuniciopn con el runner del tester
|
|
#include <TSN\\ExtraCodes\\RunnerProtDef.mqh>
|
|
//--- Funciones basicas
|
|
#include <TSN\\MQLArticles\\Utils\\Basic.mqh>
|
|
//--- Compilacion
|
|
#include <TSN\\ExtraCodes\\Func.mqh>
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Enums \ Structs \ Intefaces |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_BACKCOMPARARER_STATE
|
|
{
|
|
BACKCOMPARER_STATE_FIRST_EVALUATE = 0,
|
|
BACKCOMPARER_STATE_LAST_EVALUATE
|
|
};
|
|
|
|
//---
|
|
interface IBackResComparer
|
|
{
|
|
// true si hay diferencia false si no lo haya (todo ok)
|
|
bool CompareBack(const BackRes& back_prev, const BackRes& back_curr, string& msg);
|
|
};
|
|
|
|
//---
|
|
struct BackComparerConfig
|
|
{
|
|
MTTesterTask backtests[]; // configs de cada backtest
|
|
string expert_path_exe; // path del ea a ejecutar
|
|
string expert_path_compile; // compilar
|
|
string file_ea_comunication; // donde el EA escribe su BackRes
|
|
string folder_path_comunication_tester;
|
|
string test_name;
|
|
long chart_id_orquestador;
|
|
IBackResComparer* comparador; // quien decide si paso o no
|
|
uint timeout_ms_compile;
|
|
bool common_folder;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CBackEaComparer : public CLoggerBase
|
|
{
|
|
private:
|
|
//---
|
|
ENUM_BACKCOMPARARER_STATE m_state;
|
|
IBackResComparer* m_comparador;
|
|
|
|
//---
|
|
MTTesterTask m_backtests_config[];
|
|
int m_backtests_config_size;
|
|
|
|
//---
|
|
BackRes m_back_res_prev[];
|
|
BackRes m_back_res_current[];
|
|
int m_pos_back_res;
|
|
|
|
//---
|
|
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
|
|
// Procesar evento de mttester
|
|
void RecibirEventoFinishBack(const bool exito, const string& sparam_log);
|
|
// Mandar eventos de ejeuccion de backtet
|
|
void SendBacktestCommand();
|
|
// Compilar
|
|
bool CompileEa();
|
|
// Comparar
|
|
void CompararResultados();
|
|
|
|
|
|
|
|
public:
|
|
CBackEaComparer(void) : m_current_chart_id(ChartID()) {}
|
|
~CBackEaComparer(void);
|
|
|
|
|
|
//---
|
|
void Init(const BackComparerConfig& init);
|
|
|
|
//---
|
|
void ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
CBackEaComparer::~CBackEaComparer(void)
|
|
{
|
|
if(CheckPointer(m_comparador) == POINTER_DYNAMIC)
|
|
delete m_comparador;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CBackEaComparer::Init(const BackComparerConfig& init)
|
|
{
|
|
//---
|
|
m_expert_path_exe = init.expert_path_exe;
|
|
m_expert_path_compile = init.expert_path_compile;
|
|
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_comparador = init.comparador;
|
|
m_max_timeout_ms_compile = init.timeout_ms_compile;
|
|
m_chart_id_runner = DEFMTTesterGetChartId();
|
|
|
|
//---
|
|
m_backtests_config_size = ArrayCopyAll(m_backtests_config, init.backtests);
|
|
ArrayResize(m_back_res_prev, m_backtests_config_size);
|
|
ArrayResize(m_back_res_current, m_backtests_config_size);
|
|
|
|
//---
|
|
m_state = BACKCOMPARER_STATE_FIRST_EVALUATE;
|
|
m_pos_back_res = 0;
|
|
|
|
//---
|
|
SendBacktestCommand();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CBackEaComparer::CompararResultados(void)
|
|
{
|
|
//---
|
|
if(!::CheckPointer(m_comparador))
|
|
{
|
|
LogError("No hay comparador asignado", FUNCION_ACTUAL);
|
|
//---
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
//---
|
|
for(int i = 0; i < m_backtests_config_size; i++)
|
|
{
|
|
static string msg;
|
|
if(m_comparador.CompareBack(m_back_res_prev[i], m_back_res_current[i], msg))
|
|
{
|
|
LogError(StringFormat("Resultados difieren en config[%d], mes:\n%s", i, msg), FUNCION_ACTUAL);
|
|
//---
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//---
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_SUCCES, 0.00, m_test_name);
|
|
Remover(); // Sale
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CBackEaComparer::CompileEa(void)
|
|
{
|
|
// PATh libre tal cual
|
|
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 CBackEaComparer::SendBacktestCommand(void)
|
|
{
|
|
//---
|
|
// Aqui nos encargaremos de ejecutar los backtest inciales
|
|
//--- Corremos asi tal cual
|
|
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);
|
|
// Asignamos los campso
|
|
m_backtests_config[i].expert_path = m_expert_path_exe; // le tenemos que aumentar el .ex5
|
|
m_backtests_config[i].chart_id_from = m_current_chart_id;
|
|
|
|
//--- Creamos el archivo
|
|
if(!DEFMMTesterCreateFileWriteData(full_path, m_common_folder, m_backtests_config[i].ToString()))
|
|
{
|
|
//--- Imprimimos error
|
|
LogError(StringFormat("Fallo al guardar config para: %s", full_path), FUNCION_ACTUAL);
|
|
// Salimos
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
//--- Enviamos 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))
|
|
{
|
|
//--- Imprimimos el err
|
|
LogError(StringFormat("Fallo al enviar evento de run back para %s\nUltimo error = %d", full_path, ::GetLastError()), FUNCION_ACTUAL);
|
|
// Salimos
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CBackEaComparer::RecibirEventoFinishBack(const bool exito, const string &sparam_log)
|
|
{
|
|
//--- Check inicial
|
|
if(!exito)
|
|
{
|
|
// En caso al ejeuctar el backtest hayamos recibido un error entonces daremos por temrinado el test
|
|
LogError("Fallo al ejeuctar EA", FUNCION_ACTUAL);
|
|
// Salimos
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
//--- Cargamos resultados
|
|
if(m_state == BACKCOMPARER_STATE_FIRST_EVALUATE)
|
|
{
|
|
if(!m_back_res_prev[m_pos_back_res++].Load(m_file_ea_comunication, m_common_folder))
|
|
{
|
|
LogError("Fallo al cargar resultados de backtest de EA", FUNCION_ACTUAL);
|
|
// Salimos
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
// Ya lo tenemos cargado
|
|
if(m_pos_back_res >= m_backtests_config_size) // Superamos siguiente paso
|
|
{
|
|
m_state = BACKCOMPARER_STATE_LAST_EVALUATE; // Siguiente
|
|
m_pos_back_res = 0;
|
|
if(!CompileEa())
|
|
return;
|
|
SendBacktestCommand();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!m_back_res_current[m_pos_back_res++].Load(m_file_ea_comunication, m_common_folder))
|
|
{
|
|
LogError("Fallo al cargar resultados de backtest de EA", FUNCION_ACTUAL);
|
|
// Salimos
|
|
::EventChartCustom(m_chart_id_orquestador, CIBYLEO_ONEVENT_RES, CYBYLEO_CODE_ERROR, 0.00, m_test_name);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
// Ya lo tenemos cargado
|
|
if(m_pos_back_res >= m_backtests_config_size) // Superamos siguiente paso
|
|
{
|
|
m_pos_back_res = 0;
|
|
CompararResultados();
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CBackEaComparer::ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
|
{
|
|
//--- de vuelta
|
|
//lparam = combinacion de banderas en caso de error 0 exito total
|
|
//sparam = se empaqueta como (chartid|log)
|
|
//dparam = nada
|
|
|
|
//---
|
|
// Si se finaliza
|
|
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) // formato invalido
|
|
{
|
|
return;
|
|
}
|
|
|
|
//---
|
|
const long chart_id = long(arr[DEFMTTESTER_OUT_SPARAM_IDX_CHARTID]);
|
|
Print("Nuevo evento chart_id =", chart_id);
|
|
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_BACKCOMPARER_MQH
|