English Русский Deutsch 日本語
preview
De novato a experto: Supervisión y registro del backend de un EA con MQL5

De novato a experto: Supervisión y registro del backend de un EA con MQL5

MetaTrader 5Ejemplos |
17 0
Clemence Benjamin
Clemence Benjamin

Contenido del artículo:

  1. Introducción
  2. Implementación y código
  3. Pruebas
  4. Conclusión
  5. Lecciones clave
  6. Archivos adjuntos


Introducción

Compilar correctamente una herramienta de trading en MetaEditor 5 es un hito importante, pero es solo el principio. La compilación confirma que el código es sintácticamente válido, pero no garantiza el rendimiento, la estabilidad ni la corrección. La verdadera prueba comienza cuando ejecutamos el sistema en MetaTrader 5, ya sea en la plataforma en vivo o en el Probador de Estrategias, para ver cómo se traduce la teoría en la práctica.

En esta etapa, los desarrolladores se enfrentan a un desafío común: aunque hayamos escrito la lógica, no siempre sabemos cómo se comportará el EA en tiempo real. El programa ejecuta todas las reglas mecánicamente, produciendo a veces resultados inesperados. Para perfeccionar y optimizar nuestros sistemas, necesitamos algo más que simples resultados: necesitamos visibilidad de lo que ocurre dentro del EA, qué está ocurriendo, cuándo ocurre y en qué parte del código se origina.

Los límites del sistema de registro integrado y nuestra solución.

MetaTrader 5 proporciona dos herramientas principales para supervisar la actividad:

  • La pestaña Expertos muestra los registros generados por todos los Asesores Expertos (EA) e indicadores en ejecución.
  • La pestaña Diario, que registra los eventos del terminal y del servidor.

Si bien son útiles, tienen una limitación fundamental: todos los Asesores Expertos comparten la misma pestaña de Expertos. Aunque las líneas de registro incluyen el nombre del EA, los mensajes/salidas de varios sistemas se entremezclan rápidamente, creando desorden y confusión. Cuando hay varios Asesores Expertos activos, resulta difícil aislar los registros de una herramienta específica, lo que ralentiza la depuración y reduce la confianza en los resultados.

Aquí se muestran ejemplos de entradas de registro de expertos para tres programas diferentes, listados juntos en la misma secuencia.

2025.09.21 08:10:42.006 BEODemoEA (EURAUD,H4)   Abnormal termination
2025.09.21 08:20:15.056 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURUSD' is not synchronized
2025.09.21 08:20:40.862 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURGBP' is not synchronized
2025.09.21 08:20:44.065 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURCHF' is not synchronized
2025.09.21 08:20:44.067 Correlation Matrix 3D (EURAUD,M5)       Symbol 'EURJPY' is not synchronized
2025.09.21 08:20:44.078 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPUSD' is not synchronized
2025.09.21 08:20:44.080 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPCHF' is not synchronized
2025.09.21 08:20:44.082 Correlation Matrix 3D (EURAUD,M5)       Symbol 'GBPJPY' is not synchronized
2025.09.21 08:20:44.095 Correlation Matrix 3D (EURAUD,M5)       Symbol 'USDCHF' is not synchronized
2025.09.21 08:20:44.112 Correlation Matrix 3D (EURAUD,M5)       Symbol 'USDJPY' is not synchronized
2025.09.21 08:20:44.114 Correlation Matrix 3D (EURAUD,M5)       Symbol 'CHFJPY' is not synchronized
2025.09.21 08:20:44.114 Correlation Matrix 3D (EURAUD,M5)       Try again #0
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Some symbols not ready to use:
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURUSD 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURGBP 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       EURJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPUSD 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       GBPJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       USDCHF 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       USDJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       CHFJPY 
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Error. Symbols not synchronized.
2025.09.21 08:20:44.125 Correlation Matrix 3D (EURAUD,M5)       Error. Rates data is not loaded.
2025.09.21 09:00:00.112 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Session Alert: Sydney End at 2025.09.21 07:00
2025.09.21 10:00:00.177 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Session Alert: London Start at 2025.09.21 08:00
2025.09.21 10:05:00.172 Institutional Trading Zones (EURAUD,M5) indicator | EURAUD,5 | Bearish overlap, look to sell

Aquí es donde comienza nuestra discusión sobre la clase de Operaciones de Backend (BEO). En lugar de depender únicamente de la pestaña "Expertos" compartida, podemos diseñar un sistema dedicado de monitorización y registro que muestre y organice la información directamente en el gráfico. Al crear una clase específicamente para operaciones de backend, los desarrolladores obtienen:

  1. Una interfaz de depuración limpia y específica para EA.
  2. Visibilidad en tiempo real de las métricas de rendimiento y los eventos internos.
  3. Identificación más sencilla de errores, comportamientos anómalos y flujo de ejecución.

En el artículo de hoy, exploraremos cómo diseñar una clase de este tipo para fortalecer nuestro flujo de trabajo de depuración y brindar a los traders y desarrolladores una herramienta confiable para comprender lo que sucede en el backend de sus asesores expertos.

Estudio conceptual

La viabilidad del concepto de Operaciones de Backend (BEO, por sus siglas en inglés) depende de si MetaTrader 5 y MQL5 proporcionan suficientes puntos de acceso al sistema para la monitorización y el diagnóstico dentro de un Asesor Experto. Afortunadamente, la plataforma ofrece un amplio conjunto de funciones para acceder al estado de la cuenta, el entorno del terminal y las operaciones de negociación. Estas funciones van desde AccountInfo*() y TerminalInfo*() para detalles del entorno hasta MqlTradeRequest y MqlTradeResult para resultados de ejecución de operaciones. Esto garantiza que cualquier capa de backend que construyamos siempre pueda hacer referencia a la misma información de bajo nivel en la que se basa la propia terminal, lo que hace que el concepto sea técnicamente sólido.

Otro factor crucial es cómo se presentan estos detalles. MQL5 ofrece múltiples canales de salida: la pestaña Expertos para los registros de EA, la pestaña Diario para los mensajes del terminal y del servidor, y la visualización basada en gráficos mediante Comment(), ObjectCreate() o herramientas más avanzadas como CCanvas. Nuestra clase personalizada aprovecha estas características para redirigir los resultados de las operaciones, los códigos de error y las notas de diagnóstico hacia salidas estructuradas, lo que permite a los desarrolladores distinguir la actividad interna de un Asesor Experto (EA) de la de otro. Esto soluciona la limitación de los registros mixtos en la pestaña Expertos, donde varios programas podrían confundirse en un único flujo de mensajes.

Por último, la viabilidad de la integración se ve respaldada por las funciones de gestión de errores e historial de la plataforma. Gracias a GetLastError() y al acceso al historial, nuestra capa de sistema puede detectar tanto los problemas en tiempo real como el contexto de las operaciones anteriores, para luego mostrarlos o registrarlos de forma cómoda para el desarrollador o de forma amigable para los desarrolladores. Al organizar esto en una clase de backend dedicada, demostramos que la idea es factible y, además, abrimos la puerta a una depuración consistente, actualizaciones más sencillas y una monitorización transparente de los sistemas de negociación en MetaTrader 5.

En la siguiente sección de nuestra discusión, profundizaremos en esta idea examinando su implementación paso a paso. Analizaremos en detalle el código de la clase Backend Operations (BEO), destacando cómo captura, organiza y muestra información esencial para la depuración. Una vez establecidas las bases, procederemos a realizar pruebas sobre el gráfico, donde el sistema presentará información en tiempo real directamente en el gráfico de negociación.

Finalmente, compartiremos la solución completa para que tanto desarrolladores como traders puedan integrarla en sus propios flujos de trabajo, tendiendo un puente entre la teoría, el código y la ejecución práctica.


Implementación y código

El camino hacia las operaciones de backend comienza con un enfoque estructurado que equilibre la claridad y la eficiencia. Mediante el diseño de una clase de monitorización específica, CBEOMonitor, podemos separar la responsabilidad de realizar el seguimiento y mostrar los diagnósticos de la lógica de negociación en sí. Esto simplifica la depuración e introduce un componente reutilizable que puede integrarse en cualquier Asesor Experto sin tener que reescribir las mismas rutinas una y otra vez. El concepto clave aquí es la modularidad: aislar una potente unidad de código para que sirva a muchos sistemas diferentes.

Para dar vida a este concepto, lo implementé en dos pasos deliberados. En primer lugar, la clase en sí fue diseñada con todos los mecanismos básicos para capturar eventos, formatear la información y representarla de forma ordenada en el gráfico. Posteriormente, la integración en un EA de demostración demostró la facilidad con la que dicha clase puede incorporarse a un entorno de negociación real. Este desarrollo en dos fases hace hincapié en la eficiencia: una herramienta bien diseñada puede ampliarse a sistemas cada vez más complejos. Una vez sentadas esas bases, es hora de abrir el código y ver cómo funciona cada componente en la práctica.

Paso 1: La clase CBEOMonitor

Incluye: de qué depende el monitor y por qué.

Este monitor está construido sobre las primitivas de interfaz de usuario de MQL5 y los contenedores estándar. Las líneas #include incorporan Canvas para representar etiquetas de mapa de bits, List para un pequeño búfer circular en memoria de mensajes, Object.mqh y ChartObject.mqh para constantes y funciones auxiliares de objetos de gráficos, y Trade.mqh donde las funciones auxiliares relacionadas con las operaciones de trading son utilizadas por las funciones auxiliares de registro del monitor. Las enumeraciones definen los niveles de gravedad de los mensajes y el modo de visualización; se utilizan en todas partes para filtrar, aplicar colores y elegir el número de líneas que se van a mostrar. El uso de enumeraciones pequeñas y explícitas mantiene la API legible y evita el uso de números mágicos en el código de EA. 

#include <Object.mqh>
#include <Canvas\Canvas.mqh>
#include <Arrays\List.mqh>
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObject.mqh>

// enums
enum BEO_Level { BEO_L_INFO=0, BEO_L_SUCCESS, BEO_L_WARNING, BEO_L_ERROR, BEO_L_TRADE, BEO_L_DEBUG };
enum BEO_DisplayMode { BEO_DM_COMPACT=0, BEO_DM_EXPANDED };

BEO_Item: la unidad de información registrada

Cada entrada del registro está representada por la clase BEO_Item. Almacena el texto legible para humanos, un nivel de gravedad, una marca de tiempo con fecha y hora y una marca de tiempo con resolución de microsegundos para métricas de rendimiento del procesamiento, además de una etiqueta de origen opcional (de dónde proviene el mensaje en el EA). Almacenar los mensajes como objetos permite un búfer circular de capacidad fija (a través de CList) al tiempo que simplifica la gestión de la memoria. Esta pequeña estructura es el elemento básico para la presentación y para la salida en el registro de Expertos. 

class BEO_Item : public CObject
{
public:
   string    text;
   BEO_Level level;
   datetime  time;
   ulong     micro;
   string    origin;

   BEO_Item() { text=""; level=BEO_L_INFO; time=0; micro=0; origin=""; }
   BEO_Item(string _text, BEO_Level _lvl, datetime _t, ulong _m, string _orig = "")
     { text=_text; level=_lvl; time=_t; micro=_m; origin=_orig; }
   void CopyFrom(const BEO_Item &src)
     { text=src.text; level=src.level; time=src.time; micro=src.micro; origin=src.origin; }
};

Estado interno del monitor y esquema de colores: ¿por qué colores opacos y texto sólido?

El monitor mantiene su propio objeto CCanvas, además de la geometría, la visibilidad, el búfer y los contadores de rendimiento. Opté por mantener el texto principal totalmente opaco (blanco sólido) y utilicé colores técnicos opacos distintos para los subtítulos, los resaltados, los mensajes de éxito, de advertencia y de error. El objetivo: legibilidad predecible sobre cualquier fondo de gráfico y asignación de significados coherente (por ejemplo, m_error = rojo). Estos colores se almacenan como miembros de color y se aplican al dibujar líneas. Almacenar el nombre de la fuente y un tamaño de fuente limitado (m_font_size) evita pasar valores no compatibles a CCanvas::FontSizeSet(); por eso calculamos un CanvasFontSizeValue() seguro antes de llamar a la API de Canvas. 

// color and font members (excerpt)
color m_text;       // solid white
color m_caption;    // header/caption color
color m_error;      // error messages
string m_font;
int    m_font_size;
int    m_transparent; // canvas transparency 0..255
// CanvasFontSizeValue() clamps m_font_size into safe range used by CCanvas::FontSizeSet

Crear/Recrear/Destruir: ciclo de vida con marcador de posición visible de forma inmediata

Al crear el lienzo, se dibuja inmediatamente un pequeño marcador de posición («BEO engine initializing...») para que los usuarios obtengan una respuesta visual inmediata cuando se adjunta el EA; esto resulta muy útil para depurar problemas relacionados con la creación o la adjunción. CreateBitmapLabel() se utiliza para crear el objeto de mapa de bits del gráfico, y luego FontNameSet()/FontSizeSet()/TransparentLevelSet() configuran la representación. La función RecreateCanvas() destruye y recrea el lienzo de forma segura cuando cambia su posición o tamaño. La función Destroy() limpia tanto el lienzo como el búfer de mensajes en memoria; además, elimina cualquier etiqueta de reserva que haya quedado de fallos anteriores. Estos pasos reflejan los buenos principios de gestión de recursos de la documentación de MQL5 (crear/actualizar/eliminar objetos de gráficos y recursos de lienzo cuando sea necesario, evitar fugas). 

bool CBEOMonitor::Create(string name,int x,int y,int w,int h)
{
   m_res_name = StringFormat("BEOMON_%s", name);
   if(!m_canvas.CreateBitmapLabel(m_res_name, x, y, w, h, COLOR_FORMAT_ARGB_RAW)) return(false);
   m_canvas.FontNameSet(m_font);
   m_canvas.FontSizeSet(CanvasFontSizeValue());                     // safe/clamped
   m_canvas.TransparentLevelSet((uchar)MathMax(0, MathMin(255, m_transparent)));
   // placeholder UI so chart shows something immediately
   m_canvas.Erase(m_bg);
   m_canvas.FillRectangle(2,2,w-4,24, ARGB(255,44,44,44));
   m_canvas.TextOut(20,7,"BEO: " + name, m_caption, ALIGN_RIGHT);
   m_canvas.Update(true);
   return(true);
}

Registro, recorte del búfer y salida en la pestaña Expertos: seguimiento tanto en el gráfico como en los registros.

Cuando el EA llama a Log(...) (o a funciones auxiliares como Info()/Error()), el monitor crea un BEO_Item, lo envía al búfer CList y emite un mensaje PrintFormat() al registro de Expertos para que el mismo mensaje sea visible en la pestaña Experto estándar. El búfer se ajusta a una capacidad que permite mantener la memoria limitada. Este enfoque dual —que incluye la visualización en el gráfico y la impresión de los informes de expertos— proporciona tanto diagnósticos visuales rápidos como un registro textual permanente en el registro de la terminal. 

void CBEOMonitor::Log(string text,BEO_Level lvl,string origin)
{
   if(lvl==BEO_L_DEBUG && !m_show_debug) return;
   BEO_Item it; it.text=text; it.level=lvl; it.time=TimeCurrent(); it.micro=_nowMicros(); it.origin=origin;
   _pushItem(it);
   PrintFormat("BEO[%s] %s%s", (lvl==BEO_L_INFO?"INFO": "..."), (origin!=""?("["+origin+"] "):""), text);
   Update();
}
void CBEOMonitor::_pushItem(const BEO_Item &it)
{
   BEO_Item *p = new BEO_Item; p.CopyFrom(it); m_buf.Add(p);
   while(m_buf.Total() > m_capacity) { BEO_Item *old=(BEO_Item*)m_buf.GetNodeAtIndex(0); if(old) delete old; m_buf.Delete(0); }
}

Tiempos de ejecución: resolución de microsegundos para una elaboración de perfiles útil.

Llama a OnStartTick() al inicio del procesamiento de ticks y a OnEndTick() al final; internamente, el monitor utiliza GetMicrosecondCount() (en MQL5) o recurre a GetTickCount() multiplicado para una resolución gruesa. Mantiene una media móvil (m_tick_avg_ms) y un contador para que la visualización en el gráfico muestre lo costoso que es el procesamiento de ticks. Este análisis rápido resulta muy valioso para detectar regresiones después de agregar nuevas funciones: si el tiempo de procesamiento aumenta repentinamente, el monitor lo muestra de inmediato. 

void CBEOMonitor::OnStartTick(void) { m_last_tick_start = _nowMicros(); }
void CBEOMonitor::OnEndTick(void)
{
   if(m_last_tick_start==0) return;
   ulong endm=_nowMicros();
   double durms=(double)((endm>m_last_tick_start)?(endm-m_last_tick_start):0)/1000.0;
   m_tick_avg_ms=(m_tick_avg_ms*m_tick_count+durms)/(m_tick_count+1);
   m_tick_count++;
   m_last_tick_start=0;
   Update();
}
ulong CBEOMonitor::_nowMicros(void) const { #ifdef __MQL5__ return (ulong)GetMicrosecondCount(); #else return (ulong)GetTickCount()*1000; #endif }

Dibujo y recorte: salida legible y alineada a la derecha que no se desbordará.

_draw() compone el lienzo: borde, título, línea de rendimiento, resumen del backend de EA y líneas de mensajes recientes. Cada línea de texto se recorta mediante _clipLineToWidth() (que utiliza m_canvas.TextWidth() para medir los píxeles) para garantizar que no haya desbordamiento ni texto invisible. Todas las llamadas a TextOut() utilizan ALIGN_RIGHT según lo solicitado, y el color del texto principal es blanco sólido, mientras que los subtítulos y los resaltados utilizan colores opacos específicos. Después de renderizar, m_canvas.Update(true) actualiza el mapa de bits en el gráfico y BringToFrontInternal() restablece las propiedades del objeto gráfico para que el lienzo permanezca visible por encima de otros objetos. Este enfoque mantiene la coherencia y la legibilidad de los diagnósticos en el gráfico, independientemente del tamaño y la resolución del mismo. 

void CBEOMonitor::_draw(void)
{
   if(!m_canvas_created) return;
   m_canvas.Erase(m_bg);
   m_canvas.Rectangle(1,1,m_w-2,m_h-2,m_border);
   m_canvas.FillRectangle(2,2,m_w-4,22, ARGB(255,44,44,44));
   m_canvas.TextOut(20,6,"BEO: " + m_name, m_caption, ALIGN_RIGHT);
   // perf
   int y=28;
   string perf = StringFormat("TickAvg: %.2f ms | Ticks: %d | Mem: %d KB", m_tick_avg_ms, (int)m_tick_count, (int)m_mem_kb);
   m_canvas.TextOut(20,y, _clipLineToWidth(perf, MathMax(20,m_w-40)), m_text, ALIGN_RIGHT);
   y += 18;
   // messages (most recent first)
   for(int i=m_buf.Total()-1; i>=0 && drawn<max_lines; i--)
   {
      BEO_Item *it=_getItemAt(i);
      string line = _clipLineToWidth(TimeToString(it.time,TIME_MINUTES|TIME_SECONDS) + " " + it.text, avail_px);
      m_canvas.TextOut(20,y,line,_colorFor(it.level),ALIGN_RIGHT);
      y+=14;
      drawn++;
   }
   m_canvas.Update(true);
   BringToFrontInternal();
}

Paso 2: Ejemplo de integración de CBEOMonitor en una demostración personalizada de EA 

Encabezado del EA e inclusión de bibliotecas

En la parte superior del EA, incluimos todas las bibliotecas principales que son esenciales tanto para la ejecución de operaciones como para la visualización en el gráfico. Entre ellas se incluyen Trade.mqh, para gestionar todas las operaciones de trading; Controls\Button.mqh, para crear botones interactivos en los gráficos; y BEOMonitor.mqh, que ofrece un sólido sistema de supervisión con registro estructurado y funciones de diagnóstico.

Inmediatamente después, declaramos las entradas para que cualquier usuario que al añadir el EA al gráfico pueda ajustar su comportamiento sin modificar el código. Las entradas son valores predeterminados definidos por el desarrollador que EA lee en OnInit(); no modifican la lógica del código. Utilice nombres claros y valores predeterminados sensatos. Cuando utilice entradas de píxeles/posición (MonitorW/MonitorH), recuerde que el EA las pasará a beo.Create(...) en tiempo de ejecución, y el monitor las utilizará para crear el objeto lienzo.

#include <Trade\Trade.mqh>
#include <Controls\Button.mqh>
#include <BEOMonitor.mqh>

input string   DemoEAName        = "BEO Demo EA";
input bool     EnableMonitor     = true;
input int      MonitorX          = 10;
input int      MonitorY          = 60;
input int      MonitorW          = 400;
input int      MonitorH          = 220;
input bool     ShowDebugMessages = false;
input int      UpdateIntervalSec = 1;
input bool     AllowTrading      = false;
input double   DemoLotSize       = 0.01;
input double   DemoTakeProfit    = 30;
input double   DemoStopLoss      = 30;

Objetos globales y ayudantes

Definimos instancias globales para operar (CTrade), monitorizar (CBEOMonitor) y para nuestros botones interactivos (CButton). Estos objetos gestionarán su propio ciclo de vida, pero se hace referencia a ellos en todo el EA. También introducimos funciones auxiliares para crear etiquetas de respaldo. Estas etiquetas actúan como un mecanismo de seguridad en caso de que el lienzo del monitor no se inicialice correctamente. Proporcionan información inmediata en el gráfico, de modo que sabemos que el sistema está operativo aunque no pueda mostrarse el monitor gráfico.

CTrade      trade;
CBEOMonitor beo;

CButton      m_buyButton;
CButton      m_sellButton;

static double g_prevBid = 0.0;
static double g_prevAsk = 0.0;
static datetime g_lastLogTime = 0;

string MakeFallbackName(const string base) { return ("BEO_FALLBACK_" + base); }

Las funciones de etiquetas de respaldo (CreateFallbackLabel, UpdateFallbackLabel, RemoveFallbackLabel) permiten mostrar o actualizar mensajes en el gráfico de forma dinámica sin interrumpir el flujo del EA. Mantienen la visibilidad del estado del EA, como por ejemplo "inicializando" o "el lienzo falló", lo cual es invaluable durante las pruebas y la depuración.

Diagnóstico de gráficos

Para facilitar la monitorización, implementamos una función de diagnóstico, PrintChartObjects(). Esta función recorre todos los objetos del gráfico e imprime sus propiedades, incluyendo nombre, tipo, esquina, posición y visibilidad. Esto es fundamental para solucionar conflictos de recursos o verificar que el lienzo BEO se haya creado correctamente. Refleja nuestro principio de transparencia: saber qué hay en el gráfico evita sorpresas en tiempo de ejecución y ayuda a la reproducibilidad de las pruebas.

void PrintChartObjects()
{
   int total = ObjectsTotal(0);
   PrintFormat("BEO Diagnostic: ObjectsTotal = %d", total);
   for(int i=0;i<total;i++)
   {
      string nm = ObjectName(0,i);
      long type   = ObjectGetInteger(0,nm,OBJPROP_TYPE);
      long corner = ObjectGetInteger(0,nm,OBJPROP_CORNER);
      long xdist  = ObjectGetInteger(0,nm,OBJPROP_XDISTANCE);
      long ydist  = ObjectGetInteger(0,nm,OBJPROP_YDISTANCE);
      bool hidden = (ObjectGetInteger(0,nm,OBJPROP_HIDDEN) != 0);
      PrintFormat("  [%d] name='%s' type=%d corner=%d x=%d y=%d hidden=%d",
                  i, nm, (int)type, (int)corner, (int)xdist, (int)ydist, hidden ? 1 : 0);
   }
}

Inicialización (OnInit)

En la función OnInit, realizamos una configuración estructurada tanto para el monitor como para los botones de órdenes rápidas. Si la monitorización está habilitada, el programa intenta crear el lienzo de CBEOMonitor. A continuación, verifica su creación y, si tiene éxito, habilita la depuración y registra un mensaje de inicio. Si falla la creación del lienzo, la etiqueta de reserva garantiza que aún así se muestre información en el gráfico.

El EA también registra los precios iniciales bid/ask, utilizándolos como referencia para realizar un seguimiento de los cambios posteriores del mercado. Con estos datos, crea botones de órdenes rápidas que permiten la simulación o ejecución manual de operaciones. Este mecanismo proporciona una forma práctica de validar la lógica de negociación de forma interactiva, en lugar de depender únicamente de activadores automáticos. Para garantizar que la pantalla de monitorización se mantenga actualizada, el EA también configura un temporizador que actualiza el lienzo a intervalos regulares.

int OnInit()
{
   if(EnableMonitor)
   {
      bool created = beo.Create(DemoEAName, MonitorX, MonitorY, MonitorW, MonitorH);
      if(created && beo.IsCanvasCreated())
      {
         beo.EnableDebug(ShowDebugMessages);
         beo.Log("BEO engine initialized...", BEO_L_SUCCESS, "OnInit");
         RemoveFallbackLabel(DemoEAName);
      }
      else
      {
         CreateFallbackLabel(DemoEAName, MonitorX, MonitorY, 12, clrSilver);
      }
   }

   g_prevBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_prevAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(EnableQuickButtons)
   {
      // Create BUY and SELL buttons...
   }

   EventSetTimer(MathMax(1, UpdateIntervalSec));
   return(INIT_SUCCEEDED);
}

Desinicialización (OnDeinit)

Durante el apagado, el EA destruye limpiamente el lienzo del monitor y los botones de acceso rápido, al tiempo que elimina cualquier etiqueta de reserva. Si se modifican los parámetros, se omiten los registros innecesarios para permitir actualizaciones sin interrupciones. Al gestionar sus recursos de esta manera, el EA evita saturar el gráfico con objetos no utilizados o dejar referencias pendientes. Esta minuciosa limpieza refleja las mejores prácticas en la gestión del ciclo de vida de los sistemas de negociación automatizados.

void OnDeinit(const int reason)
{
   if(EnableMonitor && reason != REASON_PARAMETERS)
   {
      beo.Log("Shutting down demo EA", BEO_L_INFO, "OnDeinit");
      beo.ClearHistory();
      beo.Destroy();
      RemoveFallbackLabel(DemoEAName);
   }

   m_buyButton.Destroy(reason);
   m_sellButton.Destroy(reason);
   EventKillTimer();
}

Actualizaciones del temporizador (OnTimer)

El evento OnTimer gestiona las actualizaciones periódicas. Garantiza que el lienzo del monitor se recree si el lienzo no está disponible, actualiza las etiquetas de respaldo y refresca la visualización del monitor. Además, registra un mensaje periódico de estado cada 30 segundos, lo que proporciona información sobre la cantidad de mensajes, el rendimiento del procesamiento de ticks y el estado del sistema. Este mecanismo de actualización periódica hace que el EA sea robusto frente a problemas temporales de gráficos o recursos.

void OnTimer()
{
   if(!EnableMonitor) return;

   if(!beo.IsCanvasCreated())
   {
      static int tries = 0;
      tries++;
      bool ok = beo.Create(DemoEAName, MonitorX, MonitorY, MonitorW, MonitorH);
      if(ok) { /* recovered canvas */ }
      else
      {
         string txt = StringFormat("BEO fallback: msgs=%d | TickAvg=%.3f ms", beo.MessagesCount(), beo.GetTickAvgMs());
         UpdateFallbackLabel(DemoEAName, txt, MonitorX, MonitorY, 11, clrWhite);
      }
   }
   else RemoveFallbackLabel(DemoEAName);

   beo.Update(true);

   if(TimeCurrent() - g_lastLogTime >= 30)
   {
      string summary = StringFormat("Msgs:%d | TickAvg: %.3f ms | LastMicros: %llu",
                                    beo.MessagesCount(), beo.GetTickAvgMs(), beo.GetLastMicros());
      beo.Log("Periodic heartbeat: " + summary, BEO_L_DEBUG, "OnTimer");
      g_lastLogTime = TimeCurrent();
   }
}

Procesamiento de ticks (OnTick)

La función OnTick es la lógica reactiva central del EA. En cada tick del mercado, el EA actualiza el monitor, realiza un seguimiento de los cambios de oferta y demanda, y registra los movimientos de precios relevantes. Una estrategia de demostración sencilla dentro del Asesor Experto registra cambios de precio significativos y, cuando se habilita la opción AllowTrading, también puede ejecutar operaciones. Al dividir su lógica en OnStartTick y OnEndTick, el EA preserva límites claros en torno a las interacciones del monitor, reforzando la modularidad y mejorando la legibilidad.

void OnTick()
{
   if(EnableMonitor) beo.OnStartTick();

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(EnableMonitor && (bid != g_prevBid || ask != g_prevAsk))
   {
      string txt = StringFormat("Bid: %.*f Ask: %.*f",
                                (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS), bid,
                                (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS), ask);
      beo.Info(txt, "OnTick");
      g_prevBid = bid; g_prevAsk = ask;
   }

   // Simple demo strategy: log and optionally trade on price movement
}

Botones de órdenes rápidas

El sistema gestiona las interacciones del usuario a través de OnChartEvent y ExecuteQuickOrder. Al hacer clic en un botón, se activa una operación simulada o real, dependiendo del indicador AllowTrading. Esto nos permite demostrar la lógica de negociación de forma segura, integrando completamente el sistema de monitorización de operaciones de backend, que captura registros detallados y eventos de negociación para su revisión.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(sparam == "BEO_BUY_BTN") ExecuteQuickOrder(ORDER_TYPE_BUY);
      else if(sparam == "BEO_SELL_BTN") ExecuteQuickOrder(ORDER_TYPE_SELL);
   }
}

void ExecuteQuickOrder(ENUM_ORDER_TYPE orderType)
{
   if(!AllowTrading) { /* simulate */ return; }

   MqlTradeRequest req; MqlTradeResult res;
   ZeroMemory(req); ZeroMemory(res);

   if(orderType == ORDER_TYPE_BUY) { /* set buy parameters */ }
   else { /* set sell parameters */ }

   if(!OrderSend(req, res)) beo.LogErrorDetails(...);
   else beo.LogTradeDetails(res, ..., "QuickButton");
}

Este enfoque demuestra la integración completa de CBEOMonitor; cada acción importante del EA (inicialización, actualizaciones de ticks, eventos del temporizador, ejecución de operaciones e interacción con botones) genera registros estructurados, captura datos de rendimiento y permite una visualización alternativa cuando falla el lienzo del monitor. El sistema es modular, legible y robusto, lo que garantiza claridad y facilidad de mantenimiento tanto para las operaciones en tiempo real como para las pruebas.



Pruebas

Para realizar pruebas en la terminal, tanto el Asesor Experto como el archivo de cabecera que contiene la clase BEOMonitor deben haberse compilado correctamente sin errores. En la sección “Expertos” del Navegador de MetaTrader 5, estará disponible el BEODemoEA. Simplemente arrástrelo al gráfico deseado y permita que se inicialice con la configuración predeterminada. Una vez inicializado, el sistema creará automáticamente el lienzo del monitor, configurará los botones de órdenes rápidas y comenzará a procesar los datos de ticks entrantes.

En el gráfico, los resultados de la ejecución del código se visualizan en tiempo real. El lienzo mostrará las actualizaciones de oferta y demanda según los ticks entrantes, mientras que los mensajes de inicialización confirman que el monitor y los botones se crearon correctamente. Al mismo tiempo, los registros en la pestaña Expertos sirven como confirmación adicional del backend, lo que permite validar que la información mostrada coincide con las operaciones internas de EA. Esta visualización directa garantiza que las pruebas no solo sean funcionales, sino también interactivas, lo que permite obtener información inmediata sobre el comportamiento del sistema con datos de mercado en tiempo real.

Pruebas de BEO en EURUSD

Figura 1: Colocación del EA en el gráfico.

Pruebas BEO

Figura 2: Se muestra la interacción del backend con los botones de Quick Trade para operaciones simuladas.

Registro de Expertos en el terminal MetaTrader 5:

2025.09.23 12:49:41.142 BEODemoEA (USDCHF,H1)   BEOMonitor: Created canvas resource 'BEOMON_BEO Demo EA' at 10,60 size 600x220
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: beo.Create returned true
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: beo.IsCanvasCreated() = true
2025.09.23 12:49:41.170 BEODemoEA (USDCHF,H1)   BEO Demo: expected canvas name = 'BEOMON_BEO Demo EA'
2025.09.23 12:49:41.178 BEODemoEA (USDCHF,H1)   BEO Diagnostic: ObjectsTotal = 3
2025.09.23 12:49:41.179 BEODemoEA (USDCHF,H1)     [0] name='BEOMON_BEO Demo EA' type=106 corner=0 x=10 y=60 hidden=0
2025.09.23 12:49:41.179 BEODemoEA (USDCHF,H1)     [1] name='BEO_BUY_BTN' type=103 corner=0 x=10 y=300 hidden=1
2025.09.23 12:49:41.180 BEODemoEA (USDCHF,H1)     [2] name='BEO_SELL_BTN' type=103 corner=0 x=100 y=300 hidden=1
2025.09.23 12:49:41.196 BEODemoEA (USDCHF,H1)   BEO[OK] [OnInit] BEO engine initialized...
2025.09.23 12:49:41.228 BEODemoEA (USDCHF,H1)   BEO[INFO] [OnInit] Quick Order Buttons (CButton) created: BUY/SELL for ops testing
2025.09.23 12:49:59.941 BEODemoEA (USDCHF,H1)   BEO[INFO] [OnTick] Bid: 0.79537 Ask: 0.79551

Las entradas de diagnóstico anteriores confirman que el lienzo se inicializó con las coordenadas y dimensiones previstas, y que las comprobaciones de validación, como beo.Create y beo.IsCanvasCreated(), devolvieron verdadero. Esto indica que la integración entre la clase de monitor personalizada y el entorno de gráficos es estable. Además, el nombre del lienzo esperado coincidió correctamente, lo que garantizó la correcta identificación del recurso. El resumen de diagnóstico también enumeró los objetos relacionados, incluidos los botones ocultos de COMPRA y VENTA para órdenes rápidas, lo que demuestra que el sistema maneja su componente de visualización principal y gestiona los controles auxiliares de forma coherente.

Desde un punto de vista funcional, los registros de inicialización demuestran que el motor BEO entra de forma fiable en un estado operativo y que los botones de prueba de pedidos rápidos se generan según lo previsto, aunque estén ocultos durante esta fase. La transmisión en tiempo real de OnTick confirma la capacidad de capturar y mostrar datos de mercado a través del monitor, con precios de compra y venta que se transmiten en directo al espacio del lienzo.

Esto demuestra la validez del concepto: ahora podemos visualizar la actividad interna del Asesor Experto en segundo plano en su propio entorno personalizado, en lugar de depender únicamente de los registros de texto de la terminal. Si bien la base es sólida, las mejoras futuras deberían centrarse en perfeccionar la capa de presentación, mejorar la interacción con los controles de órdenes rápidas y ampliar el alcance de la retroalimentación de diagnóstico para incluir operaciones más complejas del Asesor Experto. Esto supone un comienzo prometedor, que valida la arquitectura y ofrece una plataforma sólida para el desarrollo iterativo.



Conclusión

Hemos logrado dar vida al concepto BEO, con un lienzo que ahora se renderiza en dimensiones personalizables y muestra dinámicamente las operaciones de backend del asesor experto en tiempo real. En el camino, nos enfrentamos a algunos desafíos. Inicialmente, la visibilidad del texto era deficiente porque elegimos un color transparente que se mimetizaba con el fondo; esto se corrigió posteriormente con un color sólido y más brillante para mayor claridad.

La alineación también resultó complicada: en nuestros primeros intentos, el texto se salía del borde izquierdo. Al cambiar a ALIGN_RIGHT, logramos una presentación limpia, a pesar de que convencionalmente esperamos una alineación a la izquierda en los procesadores de texto. Con estas correcciones, la visualización en el gráfico se volvió funcional y visualmente coherente, y —lo más importante— no es estática: la actividad backend del EA se muestra activamente tick a tick.

El resultado valida la idea, pero es solo una base. Resolvimos un problema clave al proporcionar al EA su propio espacio de registro especializado, separado de la pestaña Expertos del terminal, que suele estar muy concurrida, sin dejar de reconocer que el terminal ofrece una práctica función para copiar registros con fines de depuración. En la actualidad, podemos visualizar detalles importantes del backend directamente en el gráfico, pero futuras mejoras podrían ampliar la funcionalidad, ofrecer una personalización más completa de los datos de registro, filtros avanzados o incluso la copia interactiva de registros desde el lienzo. El trabajo realizado aquí sienta una base sólida, pero aún hay margen de mejora. Presentado de esta forma, el proyecto muestra lo que es posible, aunque futuras actualizaciones podrían llevarlo mucho más allá.

Explora, experimenta y amplía esta idea en tus propios proyectos. Una breve tabla con las lecciones clave y el código adjunto completan esta discusión; sus comentarios y contribuciones ayudarán a definir lo que sigue.

Lecciones clave

LecciónDescripción:
Desarrollo de clases personalizadas en MQL5La creación de una clase específica como CBEOMonitor demuestra cómo se puede encapsular una funcionalidad compleja en módulos reutilizables, lo que mejora la mantenibilidad y la escalabilidad de los sistemas de negociación.
Integración de clases personalizadas y clases de la biblioteca MQL5 integradaLa combinación de clases definidas por el usuario con componentes de la biblioteca estándar, como CTrade y CButton, demuestra la flexibilidad de MQL5 para combinar características personalizadas con una funcionalidad integrada fiable.
Integración de CanvasEl uso de un lienzo personalizado permite a los Asesores Expertos mostrar visualmente las operaciones de backend en el gráfico, creando un espacio dedicado para la monitorización sin depender de la ventana de registro del terminal.
Gestión del color y la visibilidadElegir colores sólidos adecuados en lugar de colores transparentes es fundamental para la claridad, ya que garantiza que el texto y los datos de diagnóstico permanezcan visibles bajo diferentes fondos de gráficos.
Alineación del textoLa alineación precisa evita que el texto mostrado se desplace demasiado o se coloque incorrectamente. Comprender cómo se comportan ALIGN_LEFT, ALIGN_RIGHT y los desplazamientos de gráficos ayuda a mantener una presentación limpia.
Mecanismos de respaldoImplementar etiquetas como alternativa cuando falla la creación del lienzo garantiza la solidez. Esto proporciona al menos información básica sobre el estado, incluso si no se pueden inicializar los recursos avanzados.
Diagnóstico en tiempo realAl registrar los datos de ticks, los cambios de spread y las señales de compraventa directamente en el gráfico, el Asesor Experto crea un ciclo de retroalimentación en tiempo real que facilita la depuración y la validación del rendimiento.

Archivos adjuntos

Nombre del archivoVersiónDescripción
BEOMonitor.mqh1.00Clase principal de supervisión que gestiona el lienzo, los registros y los diagnósticos visuales de las operaciones del backend del asesor experto. Ofrece métodos reutilizables para dibujar, registrar datos y generar salidas estructuradas en un gráfico.
BEODemoEA.mq51.00Asesor experto de demostración que integra CBEOMonitor para mostrar el registro en tiempo real, la visualización de diagnósticos y la realización de pruebas de órdenes rápidas con controles personalizables.

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

Archivos adjuntos |
BEOMonitor.mqh (48.41 KB)
BEODemoEA.mq5 (27.26 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Del básico al intermedio: Colas, listas y árboles (II) Del básico al intermedio: Colas, listas y árboles (II)
Este es un artículo que tú, mi estimado lector, deberás estudiar con mucha calma. Esto se debe al tipo de contenido que se explicará en él. Aunque hemos procurado mantener las cosas lo más simples y didácticas posible, el contenido presentado aquí es, sin duda, algo muy complicado para quienes se están iniciando en la programación. Sin embargo, esto no es motivo para que te desanimes o ignores lo que se explica aquí, ya que este artículo establecerá un vínculo entre dos temas completamente diferentes, aunque íntimamente relacionados.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Simulación de mercado: Position View (IX) Simulación de mercado: Position View (IX)
En este artículo, que será un punto de inflexión, comenzaremos a explorar de manera un poco más profunda la interacción entre las aplicaciones que desarrollamos para dar soporte total al sistema de repetición/simulación. Aquí vamos a analizar un problema que, por un lado, resulta bastante molesto, pero, por otro, es muy interesante de explicar y resolver. El problema es: cómo añadir las líneas de take profit y stop loss después de que fueron eliminadas, y hacerlo sin usar el terminal, sino realizando la operación directamente en el gráfico. Bien, esto, a primera vista, parece algo simple. Sin embargo, hay algunos obstáculos que superar.