2026-03-20 21:41:01 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| ResVisualizer.mqh |
|
|
|
|
|
//| Copyright 2025, Niquel Mendoza. |
|
|
|
|
|
//| https://www.mql5.com/es/users/nique_372 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2025, Niquel Mendoza."
|
|
|
|
|
#property link "https://www.mql5.com/es/users/nique_372"
|
|
|
|
|
#property strict
|
|
|
|
|
|
|
|
|
|
#ifndef AIDATATASKRUNER_UI_TRAINING_RESVISUALIZER_MQH
|
|
|
|
|
#define AIDATATASKRUNER_UI_TRAINING_RESVISUALIZER_MQH
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Include |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "ResultsTab.mqh"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Defines \ Enums \ Struct |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// Valor key para el hashmap
|
|
|
|
|
#define AIDATATASKRUNER_TRAINIG_PY_KEY(A) (A["path"].ToString()+A["type"].ToString())
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CTrainingPyVisualizer : public CTrainingResultsAlmacenador
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
//---
|
|
|
|
|
CTextBox m_text_box;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
void OnAntesClear() override final;
|
|
|
|
|
|
|
|
|
|
// Function executed each time a new element is added at index new_pos
|
|
|
|
|
void OnNewElement(int new_pos, int8_t action) override final;
|
|
|
|
|
|
|
|
|
|
// Function executed each time an element is deleted at delete_pos
|
|
|
|
|
void OnDeleteElement(int delete_pos, CJson* item, int8_t action) override final;
|
|
|
|
|
|
|
|
|
|
// Funcion on swap
|
|
|
|
|
void OnSwap(int pos1, int pos2) override final;
|
|
|
|
|
|
|
|
|
|
// Cambio masivo
|
|
|
|
|
void OnMasiveIndexChange() override final;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CTrainingPyVisualizer(void) {}
|
|
|
|
|
~CTrainingPyVisualizer(void) {}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
bool Set(int top_gap, int left_gap, int right_gap, int bottom_gap, CWindow* win, CWndCreate* wnd_create) override final;
|
|
|
|
|
void Show(const string path, const string type) override final;
|
2026-03-21 08:06:31 -05:00
|
|
|
void OnLenguajeChange() override final {} // Nada no necesitamos
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
void OnTrainingInit() override final;
|
|
|
|
|
void OnTrainingNewResult(CJson* json) override final;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
__forceinline uint8_t EventsFlags() const override final { return TRAINING_RUNNER_PY_FLAG_ON_TRAINIG | TRAINING_RUNNER_PY_FLAG_ON_INIT; }
|
2026-03-20 21:41:01 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTrainingPyVisualizer::Set(int top_gap, int left_gap, int right_gap, int bottom_gap, CWindow *win, CWndCreate *wnd_create)
|
|
|
|
|
{
|
|
|
|
|
//--- TextBox
|
|
|
|
|
m_text_box.MainPointer(win);
|
|
|
|
|
m_text_box.FontSize(10);
|
|
|
|
|
m_text_box.Font("Arial");
|
|
|
|
|
m_text_box.MultiLineMode(true);
|
|
|
|
|
m_text_box.WordWrapMode(false);
|
|
|
|
|
m_text_box.ReadOnlyMode(true);
|
|
|
|
|
m_text_box.AutoXResizeMode(true);
|
|
|
|
|
m_text_box.AutoXResizeRightOffset(right_gap);
|
|
|
|
|
m_text_box.AutoYResizeMode(true);
|
|
|
|
|
m_text_box.AutoYResizeBottomOffset(bottom_gap);
|
|
|
|
|
m_text_box.SelectedTextColor(C'61,142,240');
|
|
|
|
|
m_text_box.SelectedBackColor(clrAquamarine);
|
|
|
|
|
m_text_box.LabelColor(AIDATATASKRUNER_COLOR_TEXT_SECONDARY);
|
|
|
|
|
m_text_box.BackColor(AIDATATASKRUNER_COLOR_FONDO_TAB_PRESSED);
|
|
|
|
|
m_text_box.BackColorHover(AIDATATASKRUNER_COLOR_FONDO_TAB_PRESSED);
|
|
|
|
|
m_text_box.BackColorPressed(AIDATATASKRUNER_COLOR_FONDO_TAB_PRESSED);
|
|
|
|
|
m_base_tab.AddToElementsArray(m_base_tab_idx, m_text_box);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(!m_text_box.CreateTextBox(left_gap, top_gap))
|
|
|
|
|
{
|
|
|
|
|
GUIBASE_ERROR_CREATION(m_text_box);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
wnd_create.AddToElementsArray(0, m_text_box);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnAntesClear(void)
|
|
|
|
|
{
|
|
|
|
|
m_hash_key_to_hist_index.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnSwap(int pos1, int pos2)
|
|
|
|
|
{
|
|
|
|
|
// items ya están intercambiados cuando llega aquí
|
|
|
|
|
// items[pos1] el que ANTES estaba en pos2
|
|
|
|
|
// items[pos2] el que ANTES estaba en pos1
|
|
|
|
|
// Solo actualizar las 2 entradas del hash
|
|
|
|
|
m_hash_key_to_hist_index.TrySetValue(AIDATATASKRUNER_TRAINIG_PY_KEY(items[pos1]), pos1);
|
|
|
|
|
m_hash_key_to_hist_index.TrySetValue(AIDATATASKRUNER_TRAINIG_PY_KEY(items[pos2]), pos2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnMasiveIndexChange(void)
|
|
|
|
|
{
|
|
|
|
|
m_hash_key_to_hist_index.Clear();
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
m_hash_key_to_hist_index.Add(AIDATATASKRUNER_TRAINIG_PY_KEY(items[i]), i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnNewElement(int new_pos, char action)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
switch(action)
|
|
|
|
|
{
|
|
|
|
|
// No hay recalculo dado que estas acciones no afectan los indices de los demas
|
|
|
|
|
case MANAGER_BASE_ACTION_NEWEL_PUSH:
|
|
|
|
|
case MANAGER_BASE_ACTION_NEWEL_REPLACE:
|
|
|
|
|
{
|
|
|
|
|
if(!m_hash_key_to_hist_index.Add(AIDATATASKRUNER_TRAINIG_PY_KEY(items[new_pos]), new_pos))
|
|
|
|
|
{
|
|
|
|
|
LogError(StringFormat("No se pudo agregar la key [%s]", AIDATATASKRUNER_TRAINIG_PY_KEY(items[new_pos])), FUNCION_ACTUAL);
|
|
|
|
|
Remove(new_pos); // manda hook de ondelete
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case MANAGER_BASE_ACTION_NEWEL_INSERT:
|
|
|
|
|
{
|
|
|
|
|
// Desde la posicion que se inserto hasta el final
|
|
|
|
|
if(!m_hash_key_to_hist_index.Add(AIDATATASKRUNER_TRAINIG_PY_KEY(items[new_pos]), new_pos))
|
|
|
|
|
{
|
|
|
|
|
LogError(StringFormat("No se pudo agregar la key[%s]", AIDATATASKRUNER_TRAINIG_PY_KEY(items[new_pos])), FUNCION_ACTUAL);
|
|
|
|
|
Remove(new_pos); // manda hook de ondelete
|
|
|
|
|
// Nota: dado que el remove lo que hace es <= enotnces las clasvez volieron a su valor original
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No hay repeiticon de key todo en orden entonces lo que haremos sera modificar los valores en adelante
|
|
|
|
|
for(int i = new_pos + 1; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
m_hash_key_to_hist_index.TrySetValue(AIDATATASKRUNER_TRAINIG_PY_KEY(items[i]), i); // Cambia el indice
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnDeleteElement(int delete_pos, CJson* item, int8_t action)
|
|
|
|
|
{
|
|
|
|
|
switch(action)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
case MANAGER_BASE_ACTION_DEL_REMOVE:
|
|
|
|
|
{
|
|
|
|
|
// Eliminar del hash
|
|
|
|
|
if(!m_hash_key_to_hist_index.Remove(AIDATATASKRUNER_TRAINIG_PY_KEY(item)))
|
|
|
|
|
LogError(StringFormat("No se pudo eliminar tool[%s]", AIDATATASKRUNER_TRAINIG_PY_KEY(item)), FUNCION_ACTUAL);
|
|
|
|
|
|
|
|
|
|
// Actualizar índices desde delete_pos en adelante
|
|
|
|
|
// OJO: en este punto el array AÚN no se desplazó
|
|
|
|
|
// items[delete_pos] sigue siendo el eliminado
|
|
|
|
|
// items[delete_pos+1..total-1] son los que bajarán un índice
|
|
|
|
|
for(int i = delete_pos + 1; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
m_hash_key_to_hist_index.TrySetValue(AIDATATASKRUNER_TRAINIG_PY_KEY(items[i]), i - 1); //i-1 nuevo indice predecible
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
case MANAGER_BASE_ACTION_DEL_DELETE:
|
|
|
|
|
case MANAGER_BASE_ACTION_DEL_REPLACE:
|
|
|
|
|
case MANAGER_BASE_ACTION_DEL_REMOVE_LAST:
|
|
|
|
|
{
|
|
|
|
|
// No desplazan índices — solo eliminar del hash
|
|
|
|
|
if(!m_hash_key_to_hist_index.Remove(AIDATATASKRUNER_TRAINIG_PY_KEY(item)))
|
|
|
|
|
LogError(StringFormat("No se pudo eliminar tool[%s]", AIDATATASKRUNER_TRAINIG_PY_KEY(item)), FUNCION_ACTUAL);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
case MANAGER_BASE_ACTION_CLEAR_ITEMS:
|
|
|
|
|
break; // OnAntesClear ya limpia el hash
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 08:06:31 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnTrainingInit()
|
|
|
|
|
{
|
|
|
|
|
CleanItems(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::OnTrainingNewResult(CJson *json)
|
|
|
|
|
{
|
|
|
|
|
AddItemFast(json); // Añadifmos a fastr (aqui habra hock)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 21:41:01 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
/*
|
|
|
|
|
// type = clasificacion
|
|
|
|
|
self.m_metrics = {
|
|
|
|
|
'accuracy': accuracy,
|
|
|
|
|
'precision': precision,
|
|
|
|
|
'recall': recall,
|
|
|
|
|
'f1_score': f1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// type = regresion
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTrainingPyVisualizer::Show(const string path, const string type)
|
|
|
|
|
{
|
|
|
|
|
static int out;
|
|
|
|
|
if(m_hash_key_to_hist_index.TryGetValue(path + type, out))
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
m_text_box.ClearTextBox();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
CJsonIterator it = items[out]["metrics"].begin();
|
|
|
|
|
while(it.IsValid())
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const string key = it.Key();
|
|
|
|
|
CJsonNode val = it.Val();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
string display = "";
|
|
|
|
|
if(val.IsNumber())
|
|
|
|
|
display = ::DoubleToString(val.ToDouble(), 4);
|
|
|
|
|
else
|
|
|
|
|
display = val.ToString();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_text_box.AddLine(key + ": " + display);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
it.Next();
|
|
|
|
|
}
|
2026-03-21 08:06:31 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_text_box.Update(true);
|
2026-03-20 21:41:01 -05:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-03-21 08:06:31 -05:00
|
|
|
LogError(StringFormat("Key [%s] no existe en el hash map", (path + type)), FUNCION_ACTUAL);
|
2026-03-20 21:41:01 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // AIDATATASKRUNER_UI_TRAINING_RESVISUALIZER_MQH
|