English Русский 中文 Español Deutsch 日本語
preview
Dominando os Registros de Log (Parte 7): Como Exibir Logs Diretamente no Gráfico

Dominando os Registros de Log (Parte 7): Como Exibir Logs Diretamente no Gráfico

MetaTrader 5 — Exemplos |
136 0
joaopedrodev
joaopedrodev

Introdução

Há coisas que fazemos no calor do desenvolvimento que, sinceramente, nem deveriam virar um artigo. São soluções que surgem no momento, pequenos detalhes criados apenas para resolver algum problema incômodo. E, olha, este é exatamente um desses casos. Cheguei até a pensar: “Não, isso é simples demais, nem vale a pena compartilhar...”. Mas a verdade é que a solução acabou sendo tão útil e muito mais agradável de usar do que eu esperava que seria quase um crime guardar isso só para mim.

Se você chegou até aqui, provavelmente já conhece a Logify, uma biblioteca completa para gerenciar e armazenar logs durante o desenvolvimento de Expert Advisors (EAs) em MQL5. Uma ferramenta desenvolvida para superar, de uma vez por todas, as limitações dos logs nativos do MetaTrader 5, oferecendo mais controle, organização e profissionalismo aos desenvolvedores.

No primeiro artigo desta série, Dominando os Registros de Log (Parte 1): Conceitos Fundamentais e Primeiros Passos em MQL5, demos os primeiros passos na construção dessa biblioteca. Exploramos os fundamentos, discutimos por que depender cegamente dos logs padrão do MetaTrader é uma receita para o caos e começamos a desenvolver uma solução robusta, personalizável e escalável.

Foi justamente durante esse processo que surgiu uma ideia que, sinceramente, nem estava no planejamento. Enquanto utilizava a biblioteca, percebi como era incômodo procurar logs no terminal, abrir a aba Experts, filtrar mensagens em meio a tantas informações ou, pior ainda, perder um erro crítico porque ele sumiu da tela durante a execução. É aquela velha história de procurar uma agulha no palheiro... enquanto o palheiro está pegando fogo.

Foi então que tive uma ideia: “E se esses logs aparecessem exatamente onde fazem mais sentido? No gráfico, bem diante dos olhos do trader, onde o robô vive e respira” E não estou falando de espalhar rótulos, setas piscantes ou objetos gráficos que mais atrapalham do que ajudam. Estou falando de algo muito mais elegante, discreto e funcional: utilizar a boa e velha função Comment().

Sim, aquela função que a maioria das pessoas simplesmente ignora, utiliza apenas para depurar uma variável e depois remove do código. Pois bem, com um pouco de criatividade, ela pode ser transformada em um console de logs limpo, legível, atualizado em tempo real e extremamente útil.

E, para que isso não soe como discurso de venda, veja como funciona na prática:

Para ser sincero, eu nem pretendia escrever este artigo. Tudo começou como um recurso adicional, quase um capricho pessoal. Mas o resultado ficou tão bom, prático e satisfatório que simplesmente não fazia sentido deixá-lo escondido no meu repositório. Se você gosta de soluções simples e inteligentes que realmente resolvem problemas, fica por aqui. Depois de hoje, você nunca mais olhará para a função Comment() da mesma maneira. Vamos transformar seu gráfico em um console de logs.


Criando um novo handler

Agora que você compreendeu o propósito dessa forma elegante de visualizar logs no gráfico, vamos colocar a mão na massa. A ideia é criar um novo handler específico dentro da nossa biblioteca Logify, responsável por capturar os logs e exibi-los diretamente no gráfico utilizando a função Comment().

Dentro da pasta handlers da biblioteca, criaremos um novo arquivo chamado LogifyHandlerComment.mqh. É nele que ficará toda a lógica responsável por transformar os logs tradicionais em uma exibição dinâmica no próprio gráfico, funcionando praticamente como um console integrado ao seu robô. Ao final desta etapa, a estrutura de arquivos da biblioteca deverá estar organizada da seguinte forma:

Dentro desse arquivo, declararemos uma nova classe chamada CLogifyHandlerComment, que, assim como os demais handlers da Logify, herda da classe base CLogifyHandler. Isso mantém a arquitetura consistente, modular e alinhada ao padrão que estamos construindo desde o primeiro artigo.

O primeiro passo, como sempre, é definir as propriedades básicas do handler: sua identificação e sua finalidade. Começamos pelo construtor da classe, no qual definimos seu nome como "comment", justamente o identificador utilizado dentro da biblioteca para ativar esse tipo específico de saída de logs.

Veja a estrutura inicial da nossa classe, com todos os métodos fundamentais já declarados — Emit(), Flush() e Close() —, prontos para serem implementados nas próximas etapas:

//+------------------------------------------------------------------+
//|                                         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)
  {
  }
//+------------------------------------------------------------------+



Planejando a configuração: tamanho, moldura e título

Antes de implementar diretamente as funções principais, precisamos resolver uma questão básica de qualquer sistema que valoriza a flexibilidade: como configurar tudo isso? A boa notícia é que as opções são simples e fazem todo o sentido dentro da proposta da Logify. Nada de configurações obscuras ou parâmetros cuja finalidade ninguém entende. Aqui, o foco está no controle visual e na organização.

Nosso handler visual de logs oferecerá quatro parâmetros principais de configuração:

  • size – Define quantas linhas de log queremos visualizar no gráfico. Em outras palavras, determina o tamanho da janela de mensagens visíveis.
  • frame_style – Define o estilo da moldura que envolve os logs no gráfico. É possível escolher entre: nenhuma moldura (exibição simples e direta), moldura simples ou moldura dupla.
  • direction – Define a direção em que as mensagens serão exibidas: de cima para baixo ou de baixo para cima.
  • title – Define o título exibido na parte superior da moldura. Você pode adicionar o nome do seu EA.

Com esses parâmetros definidos, criamos uma struct chamada MqlLogifyHandleCommentConfig. Ela reúne todas essas configurações e é utilizada dentro da nossa classe CLogifyHandlerComment. Veja a estrutura principal dessa configuração:

//+------------------------------------------------------------------+
//| 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);
     }
  };

A classe CLogifyHandlerComment passa, então, a armazenar essa configuração em uma propriedade privada (m_config). Ela também disponibiliza dois métodos essenciais para trabalhar com as configurações: SetConfig(), responsável por validar os parâmetros, e GetConfig(), que retorna as configurações atuais.

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);
  }
//+------------------------------------------------------------------+


Criando o deslocamento em cascata

Afinal, por que precisamos desse deslocamento em cascata? Simples. Se queremos que os logs apareçam no gráfico com as mensagens mais recentes na parte inferior e as mais antigas subindo gradualmente até desaparecerem, precisamos de uma estrutura que funcione como uma fila dinâmica. Sem isso, cada novo log simplesmente substituiria o anterior ou, pior ainda, as mensagens se acumulariam indefinidamente, transformando o gráfico em uma bagunça.

Imagine a seguinte situação: o espaço disponível no gráfico não é infinito. Visualmente, podemos acomodar, por exemplo, apenas 10 linhas de texto antes que elas comecem a se sobrepor ou desaparecer da tela. Portanto, precisamos garantir que, a cada nova mensagem, ela ocupe a primeira posição da fila, deslocando as demais para cima. Se já tivermos atingido o limite de 10 linhas, por exemplo, a mensagem mais antiga será simplesmente descartada.

Esse comportamento é clássico em diversas aplicações e, na computação, é conhecido como buffer circular, fila deslizante ou, de maneira mais técnica, deslocamento de elementos em um array linear. Aqui, para simplificar, chamaremos esse processo de movimento em cascata porque, na prática, as informações fluem como água, linha após linha.

Como isso funciona na prática? A lógica é surpreendentemente simples, mas extremamente eficiente:

  1. Mantemos um array chamado m_logs[], que funciona como nosso “console visual”. Esse array possui um tamanho fixo, por exemplo, 10 elementos, correspondentes a 10 linhas no gráfico.
  2. Sempre que uma nova mensagem de log chega, ela precisa aparecer no início da lista, na posição 0 do array.
  3. Para isso, deslocamos os elementos existentes: o elemento da posição 8 passa para a posição 9, o da posição 7 passa para a posição 8 e assim sucessivamente... até que o elemento da posição 0 seja deslocado para a posição 1. 
  4. Feito isso, a posição 0 fica livre para receber a nova mensagem, que aparece em destaque no topo da tela.

Visualmente, esse comportamento se assemelha a um efeito dominó. Cada nova mensagem empurra a anterior uma posição adiante. Quando o espaço se esgota, a mensagem que estava no final da fila simplesmente sai, desaparecendo tanto da tela quanto da memória. Simples, organizado e eficiente.

Sem esse mecanismo, cada novo log substituiria diretamente o anterior na função Comment() ou, por outro lado, acumularíamos linhas sem qualquer controle, sobrecarregando visualmente o gráfico. Nenhuma dessas situações é desejável. Portanto, o deslocamento em cascata não é apenas uma questão estética, mas uma necessidade funcional para garantir que nossos logs exibidos no gráfico sejam úteis e legíveis.

Agora que você compreendeu perfeitamente como funciona o deslocamento em cascata, vamos colocar tudo em prática com o método Emit(). Esse método é chamado sempre que uma nova mensagem de log precisa ser processada e exibida no gráfico. E ele faz exatamente o que discutimos: aplica o deslocamento em cascata, monta todo o texto formatado — incluindo moldura, título e alinhamento — e, por fim, exibe tudo no gráfico utilizando a função nativa Comment().

Para evitar que o código fique confuso, dividimos a lógica em funções auxiliares responsáveis pela montagem da moldura, dos títulos e das linhas.

O processo consiste em três etapas simples:

  1. Filtragem por nível: se a mensagem não atingir o nível de log configurado no handler, ela será descartada.
  2. Deslocamento em cascata: desloca os logs existentes no array m_logs[] uma posição adiante, liberando a posição zero para a nova mensagem. (seguindo o deslocamento em cascata)
  3. Montagem do Comment: utiliza funções auxiliares para gerar:
  • Cabeçalho: parte superior da moldura e título, quando configurado.
  • Corpo: lista de logs, organizada de acordo com a direção configurada.
  • Rodapé: fechamento da moldura, quando configurada.

O resultado é exibido utilizando a própria função Comment() do MQL5. Para isso, utilizamos algumas funções auxiliares:

  • GetSideBorder() → Retorna o caractere da borda lateral:
    • │ para moldura simples.
    • ║ para moldura dupla.
    • "" (vazio) quando não houver moldura.
  • GetBorderTop() → Retorna a linha superior da moldura:
    • Exemplo: ┌───────┐ ou ╔═══════╗
  • GetBorderMiddle() → Retorna o separador abaixo do título:
    • Exemplo: ├───────┤ or ╠═══════╣ 
  • GetBorderBottom() → Retorna a linha inferior da moldura:
    • Exemplo: └──────┘ ou ╚═══════╝ 
  • BuildHeader() → Monta o cabeçalho com o título, quando configurado, e a moldura.
  • BuildFooter() → Gera apenas o rodapé da moldura.
  • FormatLogLines() → Formata todas as linhas de log:
    • Aplica a borda lateral, quando houver.
    • Respeita a direção configurada (LOG_DIRECTION_UP ou LOG_DIRECTION_DOWN).

Ao final, este é o 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;
  }
//+------------------------------------------------------------------+



Limpando os logs ao finalizar

Para concluir a implementação da nossa classe CLogifyHandlerComment, ainda falta um detalhe básico, mas extremamente necessário: limpar o conteúdo exibido no gráfico quando o handler for fechado. Afinal, se a função Emit() é responsável por exibir as mensagens na tela, o método Close() tem a missão oposta: remover tudo.

E quer saber qual é a melhor parte? É incrivelmente simples. Diferentemente de outros handlers, que podem precisar fechar arquivos, encerrar conexões ou liberar memória, aqui nossa tarefa consiste apenas em remover o comentário do gráfico. E o MetaTrader permite fazer isso diretamente com a própria função Comment(), passando uma string vazia como argumento, ou seja, Comment("").

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


Testando o handler CLogifyHandlerComment na prática

Teoria sem prática é apenas... teoria. Então, vamos executar nosso handler e observar como ele se comporta no gráfico do MetaTrader. Para testá-lo, criamos um script simples e direto. Nele, simulamos um cenário típico de um Expert Advisor que gera logs de diferentes níveis: desde mensagens de depuração até alertas e erros críticos.

Além disso, configuramos nosso handler para exibir os logs diretamente no gráfico, dentro de uma moldura simples Single Line, no array, a nova mensagem entra na posição 0; visualmente, conforme a direção configurada, ela pode aparecer no topo ou na base do painel.

//+------------------------------------------------------------------+
//| 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 que utilizar Sleep()? A função Sleep() não é obrigatória. Ela está presente apenas para criar pequenos intervalos entre os logs, simulando eventos que ocorrem em momentos diferentes. Isso torna o teste mais realista e o efeito visual no gráfico mais agradável. E, se você acha que apenas o texto não consegue demonstrar bem o resultado, observe a imagem abaixo para ver como tudo funciona na prática:

Vamos ver também como ficam os outros estilos de moldura disponíveis.


Com isso, nosso handler CLogifyHandlerComment está testado, validado e 100% funcional.


Conclusão

Chegamos ao final desta série de artigos sobre a construção da Logify, uma biblioteca de logs completa, robusta e totalmente personalizável para MQL5. Ao longo dessa jornada, exploramos desde os fundamentos dos sistemas de logging até a estruturação da arquitetura, a criação de handlers e a configuração de formatos, chegando finalmente a este handler visual, que exibe logs diretamente no gráfico por meio da função Comment() .

A proposta era simples, mas extremamente necessária: preencher uma lacuna existente no desenvolvimento de EAs e ferramentas no MetaTrader 5, que é a ausência de um sistema de logs adequado, flexível e bem estruturado. Agora, quem acompanhou esta série tem em mãos uma biblioteca capaz de gerar logs organizados, filtrados por níveis, formatados conforme a necessidade e até mesmo visualizados em tempo real no gráfico, de maneira elegante e prática.

Este artigo marca o encerramento da série, pelo menos por enquanto. A biblioteca está funcional, madura e bem estruturada e atende plenamente às necessidades mais comuns. No entanto, a tecnologia está em constante evolução. Sempre há espaço para melhorias, ajustes, otimizações ou até mesmo novas ideias que surgem ao longo do caminho. E, se isso acontecer — o que é bastante provável —, pode ter certeza de que apresentarei essas atualizações em novos artigos, ampliando ainda mais o potencial da Logify.

Por enquanto, o projeto cumpre seu propósito: colocar nas mãos dos desenvolvedores uma ferramenta poderosa para depurar, analisar e monitorar seus robôs e indicadores. Que a Logify ajude você a transformar o caos em organização e mensagens dispersas em informações úteis.

Até a próxima ideia.


Nome do Arquivo
Descrição
Experts/Logify/LogiftTest.mq5
Arquivo utilizado para testar os recursos da biblioteca, contendo um exemplo prático
Include/Logify/Formatter/LogifyFormatter.mqh
Classe responsável por formatar os registros de log, substituindo placeholders por valores específicos
Include/Logify/Handlers/LogifyHandler.mqh
Classe base para gerenciar os handlers de log, incluindo a configuração de níveis e o envio de logs
Include/Logify/Handlers/LogifyHandlerComment.mqh
Handler de log que envia os registros formatados diretamente para o comentário do gráfico no MetaTrader
Include/Logify/Handlers/LogifyHandlerConsole.mqh
Handler de log que envia os registros formatados diretamente para o console do terminal MetaTrader
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
Handler de log que envia os registros formatados para um banco de dados (atualmente, apenas exibe as informações por meio de uma função de impressão, mas em breve os registros serão armazenados em um banco de dados SQLite real)
Include/Logify/Handlers/LogifyHandlerFile.mqh
Handler de log que envia os registros formatados para um arquivo
Include/Logify/Utils/IntervalWatcher.mqh
Verifica se determinado intervalo de tempo já passou, permitindo criar rotinas dentro da biblioteca
Include/Logify/Logify.mqh
Classe principal para o gerenciamento de logs, integrando níveis, modelos e formatação
Include/Logify/LogifyLevel.mqh
Arquivo que define os níveis de log da biblioteca Logify, permitindo um controle detalhado
Include/Logify/LogifyModel.mqh Estrutura que modela os registros de log, incluindo informações como nível, mensagem, incluindo informações como nível, mensagem, data e hora e contexto


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18291

Arquivos anexados |
Logify.zip (21.43 KB)
Criação de um painel administrativo de negociação em MQL5 (Parte XII): Integração de uma calculadora Forex Criação de um painel administrativo de negociação em MQL5 (Parte XII): Integração de uma calculadora Forex
O cálculo preciso das principais métricas de negociação é uma parte essencial da rotina de qualquer trader. Neste artigo, veremos como integrar uma ferramenta poderosa, a calculadora Forex, ao painel de gerenciamento de operações, ampliando ainda mais as funcionalidades do nosso sistema administrativo para traders, composto por vários painéis. Calcular corretamente o risco, o tamanho da posição e o lucro potencial é fundamental para a execução de operações. A nova funcionalidade busca agilizar esses cálculos e facilitar sua realização diretamente no painel. Ao longo do artigo, exploraremos a aplicação prática do MQL5 no desenvolvimento de painéis de negociação avançados.
Técnicas do Assistente MQL5 que você deve conhecer (Parte 62): Utilizando padrões de ADX e CCI com aprendizado por reforço via TRPO Técnicas do Assistente MQL5 que você deve conhecer (Parte 62): Utilizando padrões de ADX e CCI com aprendizado por reforço via TRPO
Os osciladores ADX e CCI são indicadores de acompanhamento de tendência e momentum que podem ser combinados no desenvolvimento de um Expert Advisor. Damos continuidade ao ponto em que paramos no último artigo, analisando como o treinamento durante o uso e a atualização do modelo desenvolvido podem ser realizados por meio do aprendizado por reforço. Utilizaremos um algoritmo que ainda não abordamos nesta série, conhecido como Trust Region Policy Optimization (TRPO). E, como sempre, a montagem de Expert Advisors por meio do Assistente MQL5 nos permite preparar nossos modelos para testes de forma muito mais rápida, além de possibilitar sua distribuição e avaliação com diferentes tipos de sinais.
Criação de EAs auto-otimizáveis em MQL5 (Parte 8): Análise de múltiplas estratégias Criação de EAs auto-otimizáveis em MQL5 (Parte 8): Análise de múltiplas estratégias
Qual é a melhor maneira de combinar diferentes estratégias para criar uma estratégia de ensemble poderosa? Vamos explorar como integrar três estratégias distintas em nosso aplicativo de negociação. Os traders costumam utilizar estratégias especializadas na abertura e no fechamento de posições, e queremos descobrir se as máquinas conseguem executar essa tarefa com melhores resultados. Para começar, vamos conhecer os recursos do testador de estratégias e os princípios da programação orientada a objetos necessários para resolver esse problema.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado Técnicas do MQL5 Wizard que você deve conhecer (Parte 61): Uso de padrões do ADX e CCI com aprendizado supervisionado
Os osciladores ADX e CCI são indicadores de acompanhamento de tendência e momentum que podem ser combinados no desenvolvimento de um Expert Advisor. Veremos como essa abordagem pode ser sistematizada utilizando os três principais métodos de treinamento de Machine Learning. Os Expert Advisors montados com o MQL5 Wizard nos permitem avaliar os padrões apresentados por esses dois indicadores, e começaremos analisando como o aprendizado supervisionado pode ser aplicado a esses padrões.