English Русский 中文 Deutsch 日本語
preview
Dominando los registros (Parte 7): Cómo mostrar los registros en un gráfico

Dominando los registros (Parte 7): Cómo mostrar los registros en un gráfico

MetaTrader 5Ejemplos |
146 0
joaopedrodev
joaopedrodev

Introducción

Hay cosas que hacemos en pleno desarrollo que, sinceramente, ni siquiera estaban pensadas para dar lugar a un artículo. Algo que sucede en el momento, un detalle que surge simplemente para solucionar un problema persistente. Y mira, este es exactamente ese tipo de cosa. Incluso pensé: «No, esto es demasiado sencillo, ni siquiera merece la pena compartirlo...». Pero la verdad es que resultó ser tan útil y mucho más satisfactorio de lo que esperaba, que sería un crimen guardármelo para mí.

Si has llegado hasta aquí, probablemente ya conozcas Logify, una biblioteca completa para gestionar y almacenar registros en el desarrollo de Expert Advisors (EA) en MQL5. Una herramienta diseñada para solucionar, de una vez por todas, las limitaciones de los registros nativos de MetaTrader 5, aportando mayor control, organización y profesionalidad a los desarrolladores.

En el primer artículo de esta serie, Dominando los registros (Parte 1): Conceptos fundamentales y primeros pasos en MQL5, dimos los primeros pasos para crear esta biblioteca. Exploramos los fundamentos, analizamos por qué confiar ciegamente en los registros estándar de MetaTrader es una invitación al caos y comenzamos a dar forma a una solución robusta, personalizable y escalable.

Y fue precisamente en medio de este proceso cuando me topé con una idea que, sinceramente, ni siquiera estaba en la hoja de ruta. Mientras usaba la biblioteca, con el tiempo me di cuenta de lo incómodo que resulta buscar registros en la terminal, abrir la pestaña Expertos, filtrar mensajes entre tanto ruido o, peor aún: pasar por alto un error crítico porque desapareció de la pantalla en medio de la ejecución. Es el típico caso de buscar una aguja en un pajar... mientras el pajar está en llamas.

Fue entonces cuando pensé: ‘¿Y si estos registros estuvieran donde realmente tienen sentido? En el gráfico, delante de los ojos del trader, donde el robot vive y respira’. Y fíjate, no me refiero a dibujar etiquetas dispersas, flechas parpadeantes u objetos gráficos que más estorban que ayudan. Me refiero a algo mucho más elegante, discreto y funcional: utilizar el clásico Comment().

Sí, esa función que la mayoría de la gente ignora solemnemente, que solo se usa para depurar una variable y luego eliminarla. Bueno, con un poco de creatividad se puede transformar en una consola de registro limpia, legible, actualizada en tiempo real y absurdamente útil.

Para que no parezca un discurso de vendedor, basta con ver cómo funciona:

Para ser sincero, ni siquiera pensaba escribir este artículo. Esto surgió como un recurso adicional, casi un capricho personal. Pero resultó tan bueno, tan práctico y satisfactorio, que simplemente no tenía sentido dejarlo oculto en mi repositorio. Si te gustan las soluciones sencillas e inteligentes que realmente resuelven un problema, quédate aquí. A partir de hoy, nunca volverás a ver Comment() con los mismos ojos. Vamos a convertir tu gráfico en una consola de registro.


Creando un nuevo handler

Ahora que ya comprendes el propósito de esta elegante visualización de registros en el gráfico, pasemos a lo que realmente importa. La idea es crear un nuevo handler específico dentro de nuestra biblioteca Logify, que se encargará de capturar los registros y mostrarlos directamente en el gráfico mediante la función Comment().

Dentro de la carpeta «handlers» de la biblioteca, crearemos un nuevo archivo llamado LogifyHandlerComment.mqh. Aquí se concentrará toda la lógica encargada de transformar los registros tradicionales en una visualización dinámica en el propio gráfico; en la práctica, se trata de una consola integrada en tu robot. Al finalizar este paso, la estructura de archivos de tu biblioteca debería estar organizada de la siguiente manera:

En este archivo, declararemos una nueva clase llamada CLogifyHandlerComment, que, al igual que los demás handlers de Logify, hereda de la clase base CLogifyHandler. Esto mantiene la arquitectura coherente, modular y fiel al patrón que hemos estado desarrollando desde el primer artículo.

El primer paso, como siempre, es definir las propiedades básicas del handler: quién es y qué hace. Esto comienza en el constructor de la clase, donde definimos su nombre como «comment», que es precisamente el identificador que se utiliza dentro de la biblioteca para activar este tipo concreto de salida de registro.

Aquí está el esqueleto inicial de nuestra clase, con todos los métodos fundamentales ya declarados (Emit(), Flush() y Close()), listos para ser implementados a continuación:

//+------------------------------------------------------------------+
//|                                         LogifyHandlerComment.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "joaopedrodev"
#property link      "https://www.mql5.com/en/users/joaopedrodev"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "LogifyHandler.mqh"
//+------------------------------------------------------------------+
//| class : CLogifyHandlerComment                                    |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandlerComment                              |
//| Heritage    : CLogifyHandler                                     |
//| Description : Log handler, inserts data into chart comment.      |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandlerComment : public CLogifyHandler
  {
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
  }
//+------------------------------------------------------------------+
//| Clears or completes any pending operations                       |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Flush(void)
  {
  }
//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
  }
//+------------------------------------------------------------------+



Planificación de la configuración: tamaño, marco y título

Antes de ponernos directamente a implementar las funciones principales, debemos resolver una cuestión fundamental en cualquier sistema que valore la flexibilidad: ¿cómo se configura esto? La buena noticia es que las opciones son sencillas y encajan perfectamente en la propuesta de Logify. Sin configuraciones ni parámetros confusos que nadie sabe para qué sirven. En este caso, lo más importante es el control visual y la organización.

Nuestro handler de registros visuales ofrecerá cuatro parámetros de configuración principales:

  • size – Define cuántas líneas de registro queremos ver en el gráfico. En otras palabras, ¿qué tamaño tiene la ventana del mensaje visible?
  • frame_style – El estilo del marco que rodea el registro en el gráfico. Aquí puedes elegir entre: sin marco (simple y directo), marco simple o marco doble.
  • direction – La dirección en la que se mostrarán los mensajes: de arriba abajo o de abajo arriba.
  • title – El título que aparece en la parte superior del marco. Puedes añadir el nombre de tu EA

Teniendo en cuenta estos parámetros, creamos una estructura llamada MqlLogifyHandleCommentConfig. Esta estructura encapsula todas estas configuraciones y se utiliza dentro de nuestra clase CLogifyHandlerComment. Aquí está el núcleo de esta configuración:

//+------------------------------------------------------------------+
//| ENUMS                                                            |
//+------------------------------------------------------------------+
enum ENUM_LOG_FRAME_STYLE
  {
   LOG_FRAME_STYLE_NONE = 0,           // No rotation
   LOG_FRAME_STYLE_SINGLE,             // Rotate based on date
   LOG_FRAME_STYLE_DOUBLE,             // Rotate based on file size
  };
enum ENUM_LOG_DIRECTION
  {
   LOG_DIRECTION_UP = 0,               // Up
   LOG_DIRECTION_DOWN,                 // Down
  };
//+------------------------------------------------------------------+
//| Struct: MqlLogifyHandleComment                                   |
//+------------------------------------------------------------------+
struct MqlLogifyHandleCommentConfig
  {
   int size;                           // Space in lines that it will occupy
   ENUM_LOG_FRAME_STYLE frame_style;   // Display grid
   ENUM_LOG_DIRECTION direction;       // Direction
   string title;                       // log title
   
   //--- Default constructor
   MqlLogifyHandleCommentConfig(void)
     {
      size = 20;
      frame_style = LOG_FRAME_STYLE_SINGLE;
      direction = LOG_DIRECTION_UP;
      title = "LOGIFY";
     }
   
   //--- Destructor
   ~MqlLogifyHandleCommentConfig(void)
     {
     }

   //--- Validate configuration
   bool ValidateConfig(string &error_message)
     {
      //--- Saves the return value
      bool is_valid = true;
      
      //--- Check if size is greater than 0
      if(size <= 0)
        {
         size = 20;
         error_message = "Size must be greater than 0.";
         is_valid = false;
        }
      
      //--- Check len
      if(StringLen(title) > 40)
        {
         error_message = "Title is too long for frame. Max 40 chars.";
         is_valid = false;
        }
      
      //--- No errors found
      return(is_valid);
     }
  };

La clase CLogifyHandlerComment tiene entonces esta configuración como una propiedad privada (m_config). También expone dos métodos esenciales para trabajar con la configuración: SetConfig(), que valida los ajustes, y GetConfig(), que devuelve los ajustes actuales.

class CLogifyHandlerComment : public CLogifyHandler
  {
private:
   
   MqlLogifyHandleCommentConfig m_config;
   
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   //--- Configuration management
   void              SetConfig(MqlLogifyHandleCommentConfig &config);
   MqlLogifyHandleCommentConfig GetConfig(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Set configuration                                                |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::SetConfig(MqlLogifyHandleCommentConfig &config)
  {
   m_config = config;
   
   //--- Validade config
   string err_msg = "";
   if(!m_config.ValidateConfig(err_msg))
     {
      Print("[ERROR] ["+TimeToString(TimeCurrent())+"] Log system error: "+err_msg);
     }
   
   //--- Resize
   ArrayResize(m_logs, m_config.size);
  }
//+------------------------------------------------------------------+
//| Get configuration                                                |
//+------------------------------------------------------------------+
MqlLogifyHandleCommentConfig CLogifyHandlerComment::GetConfig(void)
  {
   return(m_config);
  }
//+------------------------------------------------------------------+


Implementando el desplazamiento en cascada

¿Por qué necesitamos este cambio en cascada? Simple. Si queremos que los registros aparezcan en el gráfico con los mensajes más recientes en la parte inferior y los más antiguos subiendo gradualmente hasta desaparecer, necesitamos una estructura que se comporte como una cola dinámica. Sin esto, cada nuevo registro simplemente sobrescribiría el anterior, o peor aún, se acumularía indefinidamente, convirtiendo el gráfico en un desastre.

Imagina esto de la siguiente manera: el espacio disponible en el gráfico no es infinito. Visualmente, solo se pueden mostrar, por ejemplo, 10 líneas de texto antes de que empiecen a superponerse o a desaparecer de la pantalla. Por lo tanto, debemos asegurarnos de que, cada vez que aparezca un nuevo mensaje, este ocupe el primer lugar de la cola, desplazando hacia arriba a los demás. Si ya hemos alcanzado el límite (10 líneas, por ejemplo), simplemente se descarta la más antigua.

Este comportamiento es habitual en varias aplicaciones y, en informática, se conoce como búfer circular, cola deslizante o, en términos más técnicos, desplazamiento de elementos en una matriz lineal. Para simplificar, lo llamaremos movimiento en cascada, ya que, de hecho, la información fluye como el agua, línea tras línea.

¿Cómo funciona en la práctica? La lógica es sorprendentemente sencilla, pero sumamente eficaz:

  1. Mantenemos una matriz llamada m_logs[], que funciona como nuestra «consola visual». Esta matriz tiene un tamaño fijo, por ejemplo, 10 elementos, es decir, 10 líneas en el gráfico.
  2. Cada vez que llegue un nuevo mensaje de registro, debe aparecer en la parte superior de la lista, en la posición 0 de la matriz.
  3. Para ello, movemos los elementos existentes: los que están en la posición 8 van a la 9, los que están en la 7 van a la 8, y así sucesivamente… hasta que los que están en la posición 0 se mueven a la 1. 
  4. Una vez hecho esto, el espacio en la posición 0 queda libre y allí insertamos el nuevo mensaje, que aparece en la parte superior de la pantalla.

Visualmente, esto se comporta como un efecto dominó. Cada nuevo mensaje desplaza al anterior una posición hacia abajo. Cuando se agota el espacio, el elemento que queda al final de la cola simplemente desaparece, desaparece tanto de la pantalla como de la memoria. Sencillo, limpio y eficiente.

Sin este mecanismo, cada nuevo registro sobrescribiría el anterior directamente en Comment(), o, por otro lado, acumularíamos líneas sin control, recargando el gráfico. Ninguno de los dos escenarios es deseable. Por lo tanto, el desplazamiento en cascada no es solo una cuestión de estética, sino una necesidad funcional para garantizar que nuestro registro en el gráfico sea útil y legible.

Ahora que ya entiendes perfectamente cómo funciona la cascada, pongamos todo esto en práctica con el método Emit(). Este método se invoca cada vez que es necesario procesar un nuevo mensaje de registro y mostrarlo en el gráfico. Y hace exactamente lo que habíamos comentado: aplica el desplazamiento en cascada, reúne todo el texto formateado (incluidos el marco, el título y la alineación), y finalmente lo presenta todo en el gráfico mediante la función nativa Comment().

Para evitar que el código resulte demasiado denso, hemos dividido la lógica en funciones auxiliares que se encargan de ensamblar el marco, los títulos y las líneas.

El proceso consta de tres sencillos pasos:

  1. Filtrar por nivel: Si el mensaje no alcanza el nivel de registro configurado en el handler, se descarta.
  2. Desplazamiento en cascada: Mueve los registros existentes en la matriz m_logs[] una posición hacia adelante, liberando la posición cero para el nuevo mensaje. (siguiendo el cambio en cascada)
  3. Construcción del comentario: Utiliza funciones auxiliares para generar:
  • Encabezado: Marco superior y título (si lo hay).
  • Cuerpo: Lista de registros, ordenados según la dirección configurada.
  • Pie de página: Cierra el marco, si está configurado.

El resultado se muestra utilizando la función Comment() propia de MQL5. Para ello, utilice algunas funciones auxiliares, que son:

  • GetSideBorder() → Devuelve el carácter del borde lateral:
    • │ para un marco simple.
    • ║ para un marco doble.
    • "" (vacío) si no hay marco.
  • GetBorderTop() → Devuelve la primera línea del marco:
    • Ejemplo: ┌───────┐ o ╔═══════╗
  • GetBorderMiddle() → Devuelve el separador que aparece debajo del título:
    • Ejemplo: ├───────┤ o ╠═══════╣ 
  • GetBorderBottom() → Devuelve la línea inferior del marco:
    • Ejemplo: └──────┘ o ╚═══════╝ 
  • BuildHeader() → Ensambla el encabezado con el título (si está configurado) y el marco.
  • BuildFooter() → Genera únicamente el pie de página del marco.
  • FormatLogLines() → Formatea todas las líneas de registro:
    • Aplica el borde lateral (si lo hay).
    • Respeta la dirección (LOG_DIRECTION_UP o LOG_DIRECTION_DOWN).

Al final, este es el código completo:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
   //--- Check if log level is allowed
   if(data.level < this.GetLevel())
     {
      return;
     }

   //--- Shift logs to maintain history
   for(int i = m_config.size-1; i > 0; i--)
     {
      m_logs[i] = m_logs[i-1];
     }
   m_logs[0] = data;

   //--- Build the complete comment
   string comment = BuildHeader();
   comment += FormatLogLines();
   comment += BuildFooter();

   //--- Display on chart
   Comment(comment);
  }
//+------------------------------------------------------------------+
//| Returns the side border character based on frame style          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetSideBorder()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE) return "│";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE) return "║";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the top border based on frame style                     |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderTop()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "┌───────────────────────────────────────────┐\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╔═══════════════════════════════════════════╗\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the middle separator based on frame style               |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderMiddle()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "├───────────────────────────────────────────┤\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╠═══════════════════════════════════════════╣\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the bottom border based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderBottom()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "└───────────────────────────────────────────┘\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╚═══════════════════════════════════════════╝\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Builds the comment header with optional title and frame         |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildHeader()
  {
   string header = "";

   if(m_config.title != "" && m_config.title != NULL)
     {
      if(m_config.frame_style == LOG_FRAME_STYLE_NONE)
        {
         header += " " + m_config.title + "\n";
         header += "─────────────────────────────────────────────\n";
        }
      else
        {
         header += GetBorderTop();
         header += GetSideBorder() + " " + m_config.title + "\n";
         header += GetBorderMiddle();
        }
     }
   else
     {
      if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
        {
         header += GetBorderTop();
        }
     }

   return header;
  }
//+------------------------------------------------------------------+
//| Builds the comment footer based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildFooter()
  {
   if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
      return GetBorderBottom();
   return "";
  }
//+------------------------------------------------------------------+
//| Formats all log lines according to direction and frame          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::FormatLogLines()
  {
   string result = "";
   string side = GetSideBorder();

   if(m_config.direction == LOG_DIRECTION_UP)
     {
      for(int i = m_config.size-1; i >= 0; i--)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }
   else // LOG_DIRECTION_DOWN
     {
      for(int i = 0; i <= m_config.size-1; i++)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }

   return result;
  }
//+------------------------------------------------------------------+



Borrar el registro al final

Para concluir la implementación de nuestra clase CLogifyHandlerComment, aún falta un detalle básico pero extremadamente necesario: borrar lo que se dibujó en el gráfico cuando se cierra el handler. Después de todo, si la función Emit() se encarga de mostrar mensajes en la pantalla, el método Close() tiene la misión opuesta: borrarlo todo.

¿Y quieres saber lo mejor? Es ridículamente simple. A diferencia de otros handlers que podrían, por ejemplo, cerrar archivos, conexiones o liberar memoria, aquí nuestra tarea consiste simplemente en eliminar el comentario del gráfico. Y MetaTrader hace esto de forma muy directa utilizando la función Comment() en sí misma, sin ningún argumento, es decir, un Comment("").

//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
   //--- Clear
   Comment("");
  }
//+------------------------------------------------------------------+


Probando en la práctica el handler CLogifyHandlerComment

La teoría sin práctica es solo… teoría. Así pues, ejecutemos nuestro handler y veamos cómo se comporta en el gráfico de MetaTrader. Para probarlo, creamos un script sencillo y directo. Aquí, simulamos un escenario típico de un Asesor Experto que activa diferentes niveles de registros: desde mensajes de depuración hasta alertas y errores críticos.

Además, configuramos el handler para mostrar los registros directamente en el gráfico, dentro de un marco de estilo simple, con los mensajes descendiendo línea por línea; es decir, los más recientes aparecen en la parte inferior y los más antiguos se desplazan hacia la parte superior.

//+------------------------------------------------------------------+
//| Import CLogify                                                   |
//+------------------------------------------------------------------+
#include <Logify/Logify.mqh>
CLogify logify;
//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Handler config
   MqlLogifyHandleCommentConfig m_config;
   m_config.size = 10;                                     // Max log lines
   m_config.frame_style = LOG_FRAME_STYLE_NONE;            // Frame style
   m_config.direction = LOG_DIRECTION_UP;                  // Log direction (up)
   m_config.title = "Expert name";                         // Log panel title
   
   //--- Create and configure handler
   CLogifyHandlerComment *handler_comment = new CLogifyHandlerComment();
   handler_comment.SetConfig(m_config);
   handler_comment.SetLevel(LOG_LEVEL_DEBUG);              // Min log level
   handler_comment.SetFormatter(new CLogifyFormatter("hh:mm:ss",
                                                      "{date_time} [{levelname}]: {msg}"));
   
   //--- Add handler to Logify
   logify.AddHandler(handler_comment);
   
   //--- Test logs
   logify.Debug("Initializing Expert Advisor...", "Init", "");
   Sleep(1500);
   logify.Debug("RSI indicator value calculated: 72.56", "Indicators", "Period: 14");
   Sleep(800);
   logify.Info("Buy order sent successfully", "Order Management", "Symbol: EURUSD, Volume: 0.1");
   Sleep(800);
   logify.Alert("Stop Loss adjusted to breakeven level", "Risk Management", "Order ID: 12345678");
   Sleep(500);
   logify.Error("Failed to send sell order", "Order Management", "Reason: Insufficient balance");
   Sleep(100);
   logify.Fatal("Failed to initialize EA: Invalid settings", "Initialization", "Missing or incorrect parameters");
   
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

¿Por qué utilizar Sleep()? La función Sleep() no es obligatoria, pero está ahí simplemente para espaciar un poco los registros, simulando que los eventos ocurren en momentos diferentes, lo que hace que la prueba sea más realista y el efecto visual en el gráfico más agradable. Y si crees que ese texto no le hace justicia, echa un vistazo a la imagen de abajo para ver cómo se ve en la práctica:

Veamos cómo luce cada uno de los demás marcos que tenemos disponibles.


Con esto, nuestro handler CLogifyHandlerComment está probado, validado y es 100% funcional.


Conclusión

Hemos llegado al final de esta serie de artículos sobre la creación de Logify, una biblioteca de registro completa, robusta y totalmente personalizable para MQL5. A lo largo de este recorrido, hemos analizado todo, desde los fundamentos de los sistemas de registro hasta la estructuración de la arquitectura, la creación de handlers y la configuración de formatos, hasta llegar a este handler visual, que muestra los registros directamente en el gráfico mediante la función Comment() .

La propuesta era sencilla, pero sumamente necesaria: subsanar una carencia existente en el desarrollo de EAs y herramientas en MetaTrader 5, a saber, la ausencia de un sistema de registro adecuado, flexible y bien diseñado. Ahora, quienes siguen esta serie tienen a su disposición una biblioteca capaz de generar registros organizados, filtrados por niveles, formateados según las necesidades e incluso visualizables en tiempo real en el gráfico, de una forma elegante y práctica.

Este artículo pone fin a la serie, al menos por ahora. La biblioteca es funcional, está bien resuelta y cubre con holgura las necesidades más comunes. Sin embargo, la tecnología es un organismo vivo. Siempre hay margen para mejoras, ajustes, optimizaciones o incluso nuevas ideas que surjan durante el proceso. Y si esto sucede, que es muy probable, pueden estar seguros de que incluiré estas actualizaciones en nuevos artículos, ampliando aún más el potencial de Logify.

Por ahora, el proyecto cumple su propósito: poner una herramienta poderosa en manos de los desarrolladores para depurar, analizar y monitorear sus robots e indicadores. Que Logify te ayude a convertir el caos en orden y los mensajes aleatorios en información útil.

Nos vemos en la próxima idea.


Nombre del archivo
Descripción
Experts/Logify/LogiftTest.mq5
Archivo donde probamos las funcionalidades de la biblioteca, que contiene un ejemplo práctico.
Include/Logify/Formatter/LogifyFormatter.mqh
Clase responsable de formatear los registros de log, reemplazando los marcadores de posición con valores específicos.
Include/Logify/Handlers/LogifyHandler.mqh
Clase base para gestionar los handlers de registros, incluyendo la configuración de niveles y el envío de registros.
Include/Logify/Handlers/LogifyHandlerComment.mqh
Handler de registros que envía registros formateados directamente al comentario en el gráfico de la terminal en MetaTrader.
Include/Logify/Handlers/LogifyHandlerConsole.mqh
Handler de registros que envía registros formateados directamente a la consola del terminal en MetaTrader.
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
Handler de registros que envía registros formateados a una base de datos (por ahora solo realiza una salida por consola, pero pronto lo guardaremos en una base de datos SQLite real).
Include/Logify/Handlers/LogifyHandlerFile.mqh
Controlador de registros que envía registros formateados a un archivo.
Include/Logify/Utils/IntervalWatcher.mqh
Comprueba si ha transcurrido un intervalo de tiempo, lo que le permite crear rutinas dentro de la biblioteca.
Include/Logify/Logify.mqh
Clase principal para la gestión de registros, integrando niveles, modelos y formato.
Include/Logify/LogifyLevel.mqh
Archivo que define los niveles de registro de la biblioteca Logify, lo que permite un control detallado.
Include/Logify/LogifyModel.mqh Estructura que modela los registros de log, incluyendo detalles como nivel, mensaje, marca de tiempo y contexto.


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18291

Archivos adjuntos |
Logify.zip (21.43 KB)
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 25): Rompefractales de doble EMA Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 25): Rompefractales de doble EMA
La acción del precio es un método fundamental para identificar configuraciones de trading rentables. Sin embargo, el seguimiento manual de los movimientos y patrones de precios puede resultar complicado y llevar mucho tiempo. Para solucionar esto, estamos desarrollando herramientas que analizan automáticamente la evolución de los precios, proporcionando señales oportunas cada vez que se detectan oportunidades potenciales. Este artículo presenta una herramienta robusta que aprovecha las rupturas fractales junto con las medias móviles exponenciales (EMA) de 14 y 200 periodos para generar señales de trading fiables, ayudando a los operadores a tomar decisiones informadas con mayor confianza.
Características del Wizard MQL5 que debe conocer (Parte 67): Uso de patrones de TRIX y Williams Percent Range (WPR) Características del Wizard MQL5 que debe conocer (Parte 67): Uso de patrones de TRIX y Williams Percent Range (WPR)
El oscilador de media móvil exponencial triple (TRIX) y el oscilador de rango porcentual de Williams son otro par de indicadores que podrían utilizarse conjuntamente dentro de un Asesor Experto MQL5. Este par de indicadores, al igual que los que hemos analizado recientemente, también es complementario, ya que TRIX define la tendencia, mientras que el indicador Williams Percent Range confirma los niveles de soporte y resistencia. Como siempre, utilizamos el asistente MQL5 para evaluar el potencial que puedan tener estos dos indicadores.
Aprendizaje automático y Data Science (Parte 42): Pronóstico de series temporales de Forex con ARIMA en Python, todo lo que necesitas saber Aprendizaje automático y Data Science (Parte 42): Pronóstico de series temporales de Forex con ARIMA en Python, todo lo que necesitas saber
ARIMA, siglas de AutoRegressive Integrated Moving Average —en español, “modelo autorregresivo integrado de media móvil”—, es un potente modelo tradicional de pronóstico de series temporales. Gracias a su capacidad para detectar picos y fluctuaciones en los datos de una serie temporal, este modelo puede realizar predicciones precisas sobre los valores siguientes. En este artículo, vamos a entender qué es, cómo funciona, qué se puede hacer con él para predecir los próximos precios del mercado con gran precisión y mucho más.
Operando con el Calendario Económico MQL5 (Parte 10): Panel arrastrable y efectos al pasar el cursor para una navegación fluida por las noticias Operando con el Calendario Económico MQL5 (Parte 10): Panel arrastrable y efectos al pasar el cursor para una navegación fluida por las noticias
En este artículo, mejoramos el Calendario Económico de MQL5 mediante la incorporación de un panel de control arrastrable que nos permite reubicar la interfaz para mejorar la visibilidad del gráfico. Implementamos efectos al pasar el cursor por los botones para mejorar la interactividad y garantizar una navegación fluida con una barra de desplazamiento posicionada dinámicamente.